#include #include "wzcsvc.h" #include "notify.h" #include "intflist.h" #include "tracing.h" #include "utils.h" #include "deviceio.h" #include "storage.h" #include "zcdblog.h" // global interfaces list. It has to be initialized to {NULL, NULL} just // to differentiate the case when the list head was never initialized. HASH g_hshHandles = {0}; // HASH handing GUID<->Handle mapping; Key = "\DEVICE\{guid}" INTF_HASHES g_lstIntfHashes = {0}; // set of hashes for all INTF_CONTEXTs HANDLE g_htmQueue = NULL; // global timer queue //----------------------------------------------------------- // Synchronization routines DWORD LstRccsReference(PINTF_CONTEXT pIntfContext) { DWORD dwErr = ERROR_INVALID_PARAMETER; DbgPrint((TRC_TRACK,"[LstRccsReference(0x%p)", pIntfContext)); if (pIntfContext) { DbgPrint((TRC_SYNC," LstRccsReference 0x%p.refCount=%d", pIntfContext, pIntfContext->rccs.nRefCount)); InterlockedIncrement(&(pIntfContext->rccs.nRefCount)); dwErr = ERROR_SUCCESS; } DbgPrint((TRC_TRACK,"LstRccsReference]=%d", dwErr)); return dwErr; } DWORD LstRccsLock(PINTF_CONTEXT pIntfContext) { DWORD dwErr = ERROR_INVALID_PARAMETER; DbgPrint((TRC_TRACK,"[LstRccsLock(0x%p)", pIntfContext)); if (pIntfContext) { EnterCriticalSection(&(pIntfContext->rccs.csMutex)); dwErr = ERROR_SUCCESS; } DbgPrint((TRC_TRACK,"LstRccsLock]=%d", dwErr)); return dwErr; } DWORD LstRccsUnlockUnref(PINTF_CONTEXT pIntfContext) { DWORD dwErr = ERROR_INVALID_PARAMETER; DbgPrint((TRC_TRACK,"[LstRccsUnlockUnref(0x%p)", pIntfContext)); if (pIntfContext) { UINT nLastCount; // before doing anything, while we're still in the critical section, // decrement the ref counter and store the result in a local variable nLastCount = InterlockedDecrement(&(pIntfContext->rccs.nRefCount)); LeaveCriticalSection(&(pIntfContext->rccs.csMutex)); // if we were the last to use this context, efectively destroy it. DbgPrint((TRC_SYNC," LstRccsUnlockUnref 0x%p.refCount=%d", pIntfContext, nLastCount)); if (nLastCount == 0) LstDestroyIntfContext(pIntfContext); dwErr = ERROR_SUCCESS; } DbgPrint((TRC_TRACK,"LstRccsUnlockUnref]=%d", dwErr)); return dwErr; } //----------------------------------------------------------- // Intializes all the internal interfaces hashes DWORD LstInitIntfHashes() { DWORD dwErr = ERROR_SUCCESS; __try { InitializeCriticalSection(&g_lstIntfHashes.csMutex); g_lstIntfHashes.bValid = TRUE; } __except(EXCEPTION_EXECUTE_HANDLER) { dwErr = GetExceptionCode(); } g_lstIntfHashes.pHnGUID = NULL; InitializeListHead(&g_lstIntfHashes.lstIntfs); g_lstIntfHashes.nNumIntfs = 0; return dwErr; } //----------------------------------------------------------- // Destructs all the internal data structures - hash & lists // This call is done after all the threads confirmed they're done. DWORD LstDestroyIntfHashes() { DWORD dwErr = ERROR_SUCCESS; DbgPrint((TRC_TRACK,"[LstDestroyIntfHashes")); // destruct whatever hashes we have HshDestructor(g_lstIntfHashes.pHnGUID); while (!IsListEmpty(&g_lstIntfHashes.lstIntfs)) { PLIST_ENTRY pEntry; PINTF_CONTEXT pIntfContext; pEntry = RemoveHeadList(&g_lstIntfHashes.lstIntfs); pIntfContext = CONTAINING_RECORD(pEntry, INTF_CONTEXT, Link); // DevioCloseIntfHandle closes the handle only if it is valid. // Otherwise noop. pIntfContext is created in LstAddIntfToList // and there, the handle is initialized to HANDLE_INVALID_VALUE. // So .. attempting to close the handle here is safe. // also, this call is done after all the threads are terminated // meaning that all the ref counts should be already balanced (set // to 1) LstDestroyIntfContext(pIntfContext); } if (g_lstIntfHashes.bValid) { g_lstIntfHashes.bValid = FALSE; DeleteCriticalSection(&g_lstIntfHashes.csMutex); } DbgPrint((TRC_TRACK,"LstDestroyIntfHashes]=%d", dwErr)); return dwErr; } //----------------------------------------------------------- // Intializes the global timer queue DWORD LstInitTimerQueue() { g_htmQueue = CreateTimerQueue(); return (g_htmQueue == NULL) ? GetLastError() : ERROR_SUCCESS; } //----------------------------------------------------------- // Destructs the global timer queue DWORD LstDestroyTimerQueue() { DbgPrint((TRC_TRACK|TRC_SYNC,"[LstDestroyTimerQueue")); if (g_htmQueue != NULL) { DeleteTimerQueueEx(g_htmQueue, INVALID_HANDLE_VALUE); g_htmQueue = NULL; } DbgPrint((TRC_TRACK|TRC_SYNC,"LstDestroyTimerQueue]")); return ERROR_SUCCESS; } //----------------------------------------------------------- // Intializes all the internal data structures. Reads the list of interfaces from // Ndisuio and gets all the parameters & OIDS. DWORD LstLoadInterfaces() { DWORD dwErr = ERROR_SUCCESS; HANDLE hNdisuio = INVALID_HANDLE_VALUE; INT i; RAW_DATA rdBuffer; UINT nRequired = QUERY_BUFFER_SIZE; rdBuffer.dwDataLen = 0; rdBuffer.pData = NULL; DbgPrint((TRC_TRACK,"[LstLoadInterfaces")); // open the handle to Ndisuio. It should be used throughout // the adapters iteration dwErr = DevioGetNdisuioHandle(&hNdisuio); // since we're going to add a bunch of interface contexts, // lock the hashes first thing to do EnterCriticalSection(&g_lstIntfHashes.csMutex); for (i = 0; dwErr == ERROR_SUCCESS; i++) { PNDISUIO_QUERY_BINDING pQueryBinding; PINTF_CONTEXT pIntfContext = NULL; // allocate as much buffer as needed by DevioGetIntfBindingByIndex if (rdBuffer.dwDataLen < nRequired) { MemFree(rdBuffer.pData); rdBuffer.pData = MemCAlloc(nRequired); if (rdBuffer.pData == NULL) { dwErr = ERROR_NOT_ENOUGH_MEMORY; break; } rdBuffer.dwDataLen = nRequired; } pQueryBinding = (PNDISUIO_QUERY_BINDING)rdBuffer.pData; // go get the binding structure for this adapter's index dwErr = DevioGetIntfBindingByIndex( hNdisuio, i, &rdBuffer); // if Ndisuio says the buffer is not large enough, increase it with 1K if (dwErr == ERROR_INSUFFICIENT_BUFFER) { // increase the buffer only if it is not obscenely large already // otherwise just skip this index and move to the next. if (nRequired < QUERY_BUFFER_MAX) { nRequired += QUERY_BUFFER_SIZE; i--; } dwErr = ERROR_SUCCESS; continue; } // if we got back NO_MORE_ITEMS then we did our job successfully if (dwErr == ERROR_NO_MORE_ITEMS) { // translate this error to success and break out dwErr = ERROR_SUCCESS; break; } // in case any other failure was returned from NDISUIO, just break out // this SHOULDN'T HAPPEN if (dwErr != ERROR_SUCCESS) { DbgAssert((FALSE, "DevioGetIntfBindingByIndex failed for interface %d with err=%d", i, dwErr)); break; } // go build the INTF_CONTEXT structure, based on // the binding information (key info for the adapter) dwErr = LstConstructIntfContext( pQueryBinding, &pIntfContext); if (dwErr == ERROR_SUCCESS) { // reference and lock this brand new context LstRccsReference(pIntfContext); LstRccsLock(pIntfContext); // add it to the hashes dwErr = LstAddIntfToHashes(pIntfContext); if (dwErr == ERROR_SUCCESS) { // and dispatch the eEventAdd dwErr = StateDispatchEvent( eEventAdd, pIntfContext, NULL); // clear up the INTFCTL_INTERNAL_BLK_MEDIACONN bit since this is not a media sense handler pIntfContext->dwCtlFlags &= ~INTFCTL_INTERNAL_BLK_MEDIACONN; } // if for any reason hashing or dispatching failed, cleanup the context here if (dwErr != ERROR_SUCCESS) LstRemoveIntfContext(pIntfContext); // we're done with this context, unlock & unref it here. LstRccsUnlockUnref(pIntfContext); } // error happened at this point, recover and go to the next interface dwErr = ERROR_SUCCESS; } // unlock the hashes here LeaveCriticalSection(&g_lstIntfHashes.csMutex); // close the handle to Ndisuio - if it was opened successfully. if (hNdisuio != INVALID_HANDLE_VALUE) CloseHandle(hNdisuio); // free memory (it handles the case when the pointer is NULL) MemFree(rdBuffer.pData); DbgPrint((TRC_TRACK,"LstLoadInterfaces]=%d", dwErr)); return dwErr; } //----------------------------------------------------------- // Constructor for the INTF_CONTEXT. Takes as parameter the binding information. // Interface's GUID constitutes the context's key info. // This call doesn't insert the new context in any hash or list DWORD LstConstructIntfContext( PNDISUIO_QUERY_BINDING pBinding, PINTF_CONTEXT *ppIntfContext) { DWORD dwErr = ERROR_SUCCESS; PINTF_CONTEXT pIntfContext; LPWSTR wszName; DWORD dwNameLen; DbgPrint((TRC_TRACK,"[LstConstructIntfContext(0x%p)", pBinding)); DbgAssert((ppIntfContext != NULL, "Invalid in/out parameter")); // zero the output param *ppIntfContext = NULL; // pIntfContext is allocated with zero_init meaning // all internal pointers are set to null pIntfContext = MemCAlloc(sizeof(INTF_CONTEXT)); if (pIntfContext == NULL) { dwErr = GetLastError(); goto exit; } // initialize context's fields, in their definition order.. // Initialize the context specific fields (links, sync, control flags, state) InitializeListHead(&pIntfContext->Link); dwErr = RccsInit(&(pIntfContext->rccs)); // reference counter is initially set to 1 if (dwErr != ERROR_SUCCESS) goto exit; pIntfContext->dwCtlFlags = (INTFCTL_ENABLED | Ndis802_11AutoUnknown); // initially, the ncstatus is "DISCONNECTED" (until wzc plumbs it down) pIntfContext->ncStatus = NCS_MEDIA_DISCONNECTED; // the state handler is initially set to NULL - it will be set to the // appropriate state when the context will be added to the state machine through // dispatching an eEventAdd event. pIntfContext->pfnStateHandler = NULL; // init the timer pIntfContext->hTimer = INVALID_HANDLE_VALUE; // if we do have a valid NDIS binding for this interface // otherwise, the following fields gets initialized as below: // hTimer <- INVALID_HANDLE_VALUE // dwIndex <- 0 // wszGuid <- NULL // wszDescr <- NULL if (pBinding != NULL) { // create an inactive timer for this interface. if (!CreateTimerQueueTimer( &(pIntfContext->hTimer), g_htmQueue, (WAITORTIMERCALLBACK)WZCTimeoutCallback, pIntfContext, TMMS_INFINITE, TMMS_INFINITE, WT_EXECUTEDEFAULT)) { dwErr = GetLastError(); goto exit; } // initialize the ndis specific fields pIntfContext->dwIndex = pBinding->BindingIndex; // Copy the interface's device name. // Device name is "\DEVICE\{guid}". We keep only the guid wszName = (LPWSTR)((LPBYTE)pBinding + pBinding->DeviceNameOffset); // the DeviceNameLength is in bytes and includes the null terminator dwNameLen = pBinding->DeviceNameLength / sizeof(WCHAR); if (dwNameLen >= 8 && !_wcsnicmp(wszName, L"\\DEVICE\\", 8)) // 8 is the # of chars in "\\DEVICE\\" { wszName += 8; dwNameLen -= 8; } if (dwNameLen > 0) { pIntfContext->wszGuid = MemCAlloc(sizeof(WCHAR)*dwNameLen); if (pIntfContext->wszGuid == NULL) { dwErr = GetLastError(); goto exit; } wcscpy(pIntfContext->wszGuid, wszName); } // Copy the interface's description.name wszName = (LPWSTR)((LPBYTE)pBinding + pBinding->DeviceDescrOffset); // the DeviceDescrLength is in bytes and includes the null terminator dwNameLen = pBinding->DeviceDescrLength; if (dwNameLen > 0) { pIntfContext->wszDescr = MemCAlloc(dwNameLen); if (pIntfContext->wszDescr == NULL) { dwErr = GetLastError(); goto exit; } wcscpy(pIntfContext->wszDescr, wszName); } } // ulMediaState, ulMediaType, ulPhysicalMediaType defaults to 0 pIntfContext->hIntf = INVALID_HANDLE_VALUE; // initialize the 802.11 specific fields pIntfContext->wzcCurrent.Length = sizeof(WZC_WLAN_CONFIG); pIntfContext->wzcCurrent.InfrastructureMode = -1; pIntfContext->wzcCurrent.AuthenticationMode = -1; pIntfContext->wzcCurrent.Privacy = -1; // wzcCurrent is all zero-ed out because of how the allocation was done // pwzcVList, pwzcPList, pwzcSList, pwzcBList all default to NULL DbgPrint((TRC_GENERIC, "Intf [%d] %S - %S", pIntfContext->dwIndex, pIntfContext->wszGuid, pIntfContext->wszDescr)); exit: // if there was any error hit, clear up all resources allocated so far if (dwErr != ERROR_SUCCESS) { if (pIntfContext != NULL) { // this was a brand new context so there should be no timer queued for it if (pIntfContext->hTimer != NULL) DeleteTimerQueueTimer(g_htmQueue, pIntfContext->hTimer, INVALID_HANDLE_VALUE); MemFree(pIntfContext->wszDescr); MemFree(pIntfContext->wszGuid); } MemFree(pIntfContext); } else { // if success, copy out the new context *ppIntfContext = pIntfContext; } DbgPrint((TRC_TRACK,"LstConstructIntfContext(->0x%p)]=%d", *ppIntfContext, dwErr)); return dwErr; } //----------------------------------------------------------- // Prepares a context for the destruction: // - Deletes any attached timer, making sure no other timer routines will be fired. // - Removes the context from any hash, making sure no one else will find the context // - Decrements the reference counter such that the context will be destroyed when unrefed. // This function is called while holding the critical section on the interface. DWORD LstRemoveIntfContext( PINTF_CONTEXT pIntfContext) { DWORD dwErr = ERROR_SUCCESS; PINTF_CONTEXT pRemovedIntfContext = NULL; DbgPrint((TRC_TRACK,"[LstRemoveIntfContext(0x%p)", pIntfContext)); // synchronously delete any timer associated with the context. // Since the timer routine is lightweight there is no risk of deadlock pIntfContext->dwCtlFlags &= ~INTFCTL_INTERNAL_TM_ON; if (pIntfContext->hTimer != INVALID_HANDLE_VALUE) { HANDLE hTimer = pIntfContext->hTimer; pIntfContext->hTimer = INVALID_HANDLE_VALUE; DeleteTimerQueueTimer(g_htmQueue, hTimer, INVALID_HANDLE_VALUE); } // do the removal by passing down the guid formatted as "{guid}" // and expecting back the interface context in pIntfContext dwErr = LstRemIntfFromHashes(pIntfContext->wszGuid, &pRemovedIntfContext); DbgAssert((pIntfContext == pRemovedIntfContext, "The context removed from hashes doesn't match!")); // decrement the reference counter of the interface. This is what will make the context to be // effectively destroyed when the last thread unreference it. if (pIntfContext->rccs.nRefCount != 0) InterlockedDecrement(&(pIntfContext->rccs.nRefCount)); DbgPrint((TRC_TRACK,"LstRemoveIntfContext]=%d", dwErr)); return dwErr; } //----------------------------------------------------------- // Destructs the INTF_CONTEXT clearing all the resources allocated for it // This call doesn't remove this context from any hash or list DWORD LstDestroyIntfContext(PINTF_CONTEXT pIntfContext) { DWORD dwErr = ERROR_SUCCESS;; DbgPrint((TRC_TRACK,"[LstDestroyIntfContext(0x%p)", pIntfContext)); if (pIntfContext == NULL) { dwErr = ERROR_INVALID_PARAMETER; goto exit; } // the destroy call is made when it is sure thing the context needs to be // deleted (either something wrong happened while loading the context or its // ref counter reached 0). There is no point in testing the ref counter again. if (pIntfContext->hTimer != INVALID_HANDLE_VALUE) DeleteTimerQueueTimer(g_htmQueue, pIntfContext->hTimer, NULL); dwErr = DevioCloseIntfHandle(pIntfContext); MemFree(pIntfContext->wszGuid); MemFree(pIntfContext->wszDescr); MemFree(pIntfContext->pwzcVList); MemFree(pIntfContext->pwzcPList); WzcCleanupWzcList(pIntfContext->pwzcSList); WzcSSKFree(pIntfContext->pSecSessionKeys); WzcCleanupWzcList(pIntfContext->pwzcBList); // since rccs.nRefCount reached 0, this means there is absolutely // no other thread referencing this object and that no one will // ever be able to reference it again. Getting to 0 means at least // one thread explicitly called LstDestroyIntfContext after removed // the object from the internal hashes. RccsDestroy(&pIntfContext->rccs); // at the end clear the interface context entirely MemFree(pIntfContext); exit: DbgPrint((TRC_TRACK,"LstDestroyIntfContext]=%d", dwErr)); return dwErr; } //----------------------------------------------------------- // Returns the number of contexts enlisted in the service DWORD LstNumInterfaces() { return g_lstIntfHashes.nNumIntfs; } //----------------------------------------------------------- // Inserts the given context in all the internal hashes // This call assumes the hashes are locked by the caller DWORD LstAddIntfToHashes(PINTF_CONTEXT pIntf) { DWORD dwErr = ERROR_SUCCESS; DbgPrint((TRC_TRACK,"[LstAddIntfToHashes(0x%p)", pIntf)); DbgAssert((pIntf != NULL, "Cannot insert NULL context into hashes!")) // Insert this interface in the GUID hash dwErr = HshInsertObjectRef( g_lstIntfHashes.pHnGUID, pIntf->wszGuid, pIntf, &g_lstIntfHashes.pHnGUID); if (dwErr == ERROR_SUCCESS) { // inserting to tail insures an ascending ordered list on dwIndex // not that it matters :o) InsertTailList(&g_lstIntfHashes.lstIntfs, &(pIntf->Link)); // everything went out successfully, so increment the global number // of interfaces. g_lstIntfHashes.nNumIntfs++; } DbgPrint((TRC_TRACK,"LstAddIntfToHashes]=%d", dwErr)); return dwErr; } //----------------------------------------------------------- // Removes the context referenced by GUID from all the internal hashes. // The GUID is expected to be in the format "{guid}" // Returns in ppIntfContext the object that was removed from all hashes. // This call assumes the hashes are locked already DWORD LstRemIntfFromHashes(LPWSTR wszGuid, PINTF_CONTEXT *ppIntfContext) { DWORD dwErr; PHASH_NODE pNode; PINTF_CONTEXT pIntfContext = NULL; DbgPrint((TRC_TRACK,"[LstRemIntfFromHashes(%S)", wszGuid == NULL? L"(null)" : wszGuid)); DbgAssert((wszGuid != NULL, "Cannot clear NULL GUID from hashes!")); DbgAssert((ppIntfContext != NULL, "Invalid in/out parameter")); // get to the hash node dwErr = HshQueryObjectRef( g_lstIntfHashes.pHnGUID, wszGuid, &pNode); // if there is such a context // in the current hash, it needs to go away if (dwErr == ERROR_SUCCESS) { // remove this node from the Guid hash. We are already in its critical section dwErr = HshRemoveObjectRef( g_lstIntfHashes.pHnGUID, pNode, &pIntfContext, &g_lstIntfHashes.pHnGUID); // this is expected to succeed DbgAssert((dwErr == ERROR_SUCCESS, "Error %d while removing node 0x%p from GUID hash!!", dwErr, pNode)); } // if the context is not in the Guids hash, it is nowhere else // so go next only in case of success. if (dwErr == ERROR_SUCCESS) { PINTF_CONTEXT pIntfContextDup; // remove the context from the linked list RemoveEntryList(&pIntfContext->Link); // and initialize the pointer. InitializeListHead(&pIntfContext->Link); // decrement the global interfaces count g_lstIntfHashes.nNumIntfs--; } *ppIntfContext = pIntfContext; DbgPrint((TRC_TRACK,"LstRemIntfFromHashes(->0x%p)]=%d", *ppIntfContext, dwErr)); return dwErr; } //----------------------------------------------------------- // Returns an array of *pdwNumIntfs INTF_KEY_ENTRY elements. // The INTF_KEY_ENTRY contains whatever information identifies // uniquely an adapter. Currently it includes just the GUID in // the format "{guid}" DWORD LstGetIntfsKeyInfo(PINTF_KEY_ENTRY pIntfs, LPDWORD pdwNumIntfs) { DWORD dwErr = ERROR_SUCCESS; UINT nIntfIdx; PLIST_ENTRY pEntry; DbgPrint((TRC_TRACK,"[LstGetIntfsKeyInfo(0x%p,%d)", pIntfs, *pdwNumIntfs)); // lock the hash during enumeration EnterCriticalSection(&g_lstIntfHashes.csMutex); for (pEntry = g_lstIntfHashes.lstIntfs.Flink, nIntfIdx = 0; pEntry != &g_lstIntfHashes.lstIntfs && nIntfIdx < *pdwNumIntfs; pEntry = pEntry->Flink, nIntfIdx++) { PINTF_CONTEXT pIntfContext; // no need to lock this context since we're already holding the hashes. // no one can destroy the interface context now pIntfContext = CONTAINING_RECORD(pEntry, INTF_CONTEXT, Link); if (pIntfContext->wszGuid != NULL) { pIntfs[nIntfIdx].wszGuid = RpcCAlloc((wcslen(pIntfContext->wszGuid)+1)*sizeof(WCHAR)); if (pIntfs[nIntfIdx].wszGuid == NULL) { dwErr = GetLastError(); goto exit; } wcscpy(pIntfs[nIntfIdx].wszGuid, pIntfContext->wszGuid); } else { pIntfs[nIntfIdx].wszGuid = NULL; } } exit: // unlock the hash now LeaveCriticalSection(&g_lstIntfHashes.csMutex); if (dwErr != ERROR_SUCCESS) { UINT i; // if an error occured, rollback whatever we already did for (i = 0; iwszGuid, &pNode); if (dwErr == ERROR_SUCCESS) { pIntfContext = pNode->pObject; // bump up the reference counter since we're going // to work with this object LstRccsReference(pIntfContext); } LeaveCriticalSection(&g_lstIntfHashes.csMutex); } else dwErr = ERROR_ARENA_TRASHED; // a failure at this point, means there was no context // to lock so we can safely go to 'exit' if (dwErr != ERROR_SUCCESS) goto exit; // Lock the context now LstRccsLock(pIntfContext); // we can safely assume any living INTF_CONTEXT will have the correct // information for all the NDIS parameters below. So return them // unconditionally to the caller if ((dwInFlags & INTF_DESCR) && (pIntfContext->wszDescr) != NULL) { pIntfEntry->wszDescr = RpcCAlloc(sizeof(WCHAR)*(wcslen(pIntfContext->wszDescr)+1)); if (pIntfEntry->wszDescr != NULL) { wcscpy(pIntfEntry->wszDescr, pIntfContext->wszDescr); dwOutFlags |= INTF_DESCR; } else dwErr = GetLastError(); } if (dwInFlags & INTF_NDISMEDIA) { pIntfEntry->ulMediaState = pIntfContext->ulMediaState; pIntfEntry->ulMediaType = pIntfContext->ulMediaType; pIntfEntry->ulPhysicalMediaType = pIntfContext->ulPhysicalMediaType; dwOutFlags |= INTF_NDISMEDIA; } if (dwInFlags & INTF_ALL_FLAGS) { DWORD dwActualFlags = dwInFlags & INTF_ALL_FLAGS; pIntfEntry->dwCtlFlags = pIntfContext->dwCtlFlags & dwActualFlags; dwOutFlags |= dwActualFlags; } // copy out the StSSIDList if requested if (dwInFlags & INTF_PREFLIST) { pIntfEntry->rdStSSIDList.dwDataLen = 0; pIntfEntry->rdStSSIDList.pData = NULL; // it could happen we don't have any static entry. If so, dwOutFlags // needs to be set correctly saying "success" if (pIntfContext->pwzcPList != NULL) { UINT nBytes; // see how much memory is needed to store all the static SSIDs nBytes = FIELD_OFFSET(WZC_802_11_CONFIG_LIST, Config) + pIntfContext->pwzcPList->NumberOfItems * sizeof(WZC_WLAN_CONFIG); // allocate buffer large enough for all static SSIDs pIntfEntry->rdStSSIDList.pData = RpcCAlloc(nBytes); if (pIntfEntry->rdStSSIDList.pData != NULL) { // set the memory size in this RAW_DATA pIntfEntry->rdStSSIDList.dwDataLen = nBytes; // copy the whole WZC_802_11_CONFIG_LIST of static SSIDs CopyMemory( pIntfEntry->rdStSSIDList.pData, pIntfContext->pwzcPList, nBytes); // mark "success" dwOutFlags |= INTF_PREFLIST; } else if (dwErr == ERROR_SUCCESS) dwErr = GetLastError(); } else { // still, if no static SSID defined, this is seen as "success" dwOutFlags |= INTF_PREFLIST; } } // the 802.11 parameters are valid only if the context's state is not {SSr} if (pIntfContext->pfnStateHandler != StateSoftResetFn) { if (dwInFlags & INTF_INFRAMODE) { pIntfEntry->nInfraMode = pIntfContext->wzcCurrent.InfrastructureMode; dwOutFlags |= INTF_INFRAMODE; } if (dwInFlags & INTF_AUTHMODE) { pIntfEntry->nAuthMode = pIntfContext->wzcCurrent.AuthenticationMode; dwOutFlags |= INTF_AUTHMODE; } if (dwInFlags & INTF_WEPSTATUS) { pIntfEntry->nWepStatus = pIntfContext->wzcCurrent.Privacy; dwOutFlags |= INTF_WEPSTATUS; } // copy out the BSSID if requested if (dwInFlags & INTF_BSSID) { pIntfEntry->rdBSSID.dwDataLen = 0; pIntfEntry->rdBSSID.pData = RpcCAlloc(sizeof(NDIS_802_11_MAC_ADDRESS)); if (pIntfEntry->rdBSSID.pData != NULL) { pIntfEntry->rdBSSID.dwDataLen = sizeof(NDIS_802_11_MAC_ADDRESS); CopyMemory( pIntfEntry->rdBSSID.pData, &pIntfContext->wzcCurrent.MacAddress, pIntfEntry->rdBSSID.dwDataLen); dwOutFlags |= INTF_BSSID; } else if (dwErr == ERROR_SUCCESS) dwErr = GetLastError(); } // copy out the SSID if requested if (dwInFlags & INTF_SSID) { pIntfEntry->rdSSID.dwDataLen = 0; pIntfEntry->rdSSID.pData = NULL; // normally there should be an SSID so set the dwOutFlags // for this field only if it exists if (pIntfContext->wzcCurrent.Ssid.SsidLength != 0) { pIntfEntry->rdSSID.pData = RpcCAlloc(pIntfContext->wzcCurrent.Ssid.SsidLength); if (pIntfEntry->rdSSID.pData != NULL) { pIntfEntry->rdSSID.dwDataLen = pIntfContext->wzcCurrent.Ssid.SsidLength; CopyMemory( pIntfEntry->rdSSID.pData, pIntfContext->wzcCurrent.Ssid.Ssid, pIntfContext->wzcCurrent.Ssid.SsidLength); dwOutFlags |= INTF_SSID; } else if (dwErr == ERROR_SUCCESS) dwErr = GetLastError(); } } // copy out the BSSIDList if requested if (dwInFlags & INTF_BSSIDLIST) { pIntfEntry->rdBSSIDList.dwDataLen = 0; pIntfEntry->rdBSSIDList.pData = NULL; // normally there should be a visible list so set the dwOutFlags // for this field only if it exists if (pIntfContext->pwzcVList != NULL) { UINT nBytes; // see how much memory is needed to store all the configurations nBytes = FIELD_OFFSET(WZC_802_11_CONFIG_LIST, Config) + pIntfContext->pwzcVList->NumberOfItems * sizeof(WZC_WLAN_CONFIG); // allocate buffer large enough to hold all the configurations pIntfEntry->rdBSSIDList.pData = RpcCAlloc(nBytes); if (pIntfEntry->rdBSSIDList.pData != NULL) { // set the memory size in this RAW_DATA pIntfEntry->rdBSSIDList.dwDataLen = nBytes; // copy the whole WZC_802_11_CONFIG_LIST CopyMemory( pIntfEntry->rdBSSIDList.pData, pIntfContext->pwzcVList, nBytes); dwOutFlags |= INTF_BSSIDLIST; } else if (dwErr == ERROR_SUCCESS) dwErr = GetLastError(); } } } // if the context's state is {SSr} and some OIDs are requested, don't fail, but the bits // corresponding to the OIDs will all be nulled out. This is the indication of "pending" LstRccsUnlockUnref(pIntfContext); exit: if (pdwOutFlags != NULL) { *pdwOutFlags = dwOutFlags; DbgPrint((TRC_GENERIC,"Sending OutFlags = 0x%x", *pdwOutFlags)); } DbgPrint((TRC_TRACK, "LstQueryInterface]=%d", dwErr)); return dwErr; } //----------------------------------------------------------- // Sets the specified parameters on the specified adapter. // [in] dwInFlags specifies the parameters to be set. (see // bitmasks INTF_* // [in] pIntfEntry should contain the GUID of the adapter and // all the additional parameters to be set as specified // in dwInFlags // [out] pdwOutFlags provides an indication on the params that // were successfully set to the adapter // Each parameter for which the driver says that was set successfully // is copied into the interface's context. DWORD LstSetInterface( DWORD dwInFlags, PINTF_ENTRY pIntfEntry, LPDWORD pdwOutFlags) { DWORD dwErr = ERROR_SUCCESS; DWORD dwLErr; PHASH_NODE pNode = NULL; PINTF_CONTEXT pIntfContext; DWORD dwOutFlags = 0; DbgPrint((TRC_TRACK, "[LstSetInterface")); if (pIntfEntry->wszGuid == NULL) { if (g_wzcInternalCtxt.bValid) { EnterCriticalSection(&g_wzcInternalCtxt.csContext); pIntfContext = g_wzcInternalCtxt.pIntfTemplate; LstRccsReference(pIntfContext); LeaveCriticalSection(&g_wzcInternalCtxt.csContext); } else dwErr = ERROR_ARENA_TRASHED; } else { if (g_lstIntfHashes.bValid) { EnterCriticalSection(&g_lstIntfHashes.csMutex); dwErr = HshQueryObjectRef( g_lstIntfHashes.pHnGUID, pIntfEntry->wszGuid, &pNode); if (dwErr == ERROR_SUCCESS) { pIntfContext = pNode->pObject; LstRccsReference(pIntfContext); } LeaveCriticalSection(&g_lstIntfHashes.csMutex); } else dwErr = ERROR_ARENA_TRASHED; } // a failure at this point, means there was no context // to lock so we can safely go to 'exit' if (dwErr != ERROR_SUCCESS) goto exit; LstRccsLock(pIntfContext); // 1) Set the new public Control flags, if specified if (dwInFlags & INTF_ALL_FLAGS) { DWORD dwActualFlags = dwInFlags & INTF_ALL_FLAGS; DWORD dwSupp = (pIntfContext->dwCtlFlags & INTFCTL_OIDSSUPP); pIntfContext->dwCtlFlags &= ~dwActualFlags; pIntfContext->dwCtlFlags |= pIntfEntry->dwCtlFlags & dwActualFlags; // retain the original INTFCTL_OIDSSUPP bit pIntfContext->dwCtlFlags |= dwSupp; dwOutFlags |= dwActualFlags; } // 2) copy the list of Static SSID (if requested to be set) as below: // Allocate the memory needed for the new static SSIDs list (if needed) // If successful, copy in the new buffer the new list of static SSIDs, clear up // whatever old list we had and put the new one in the interface's context if (dwInFlags & INTF_PREFLIST) { PWZC_802_11_CONFIG_LIST pNewPList; // MemCAlloc handles the case when size is 0 (returns NULL) pNewPList = (PWZC_802_11_CONFIG_LIST)MemCAlloc(pIntfEntry->rdStSSIDList.dwDataLen); if (pIntfEntry->rdStSSIDList.dwDataLen != 0 && pNewPList == NULL) { dwLErr = GetLastError(); } else { // .. copy the data in the new buffer if there is any if (pNewPList != NULL) { CopyMemory( pNewPList, pIntfEntry->rdStSSIDList.pData, pIntfEntry->rdStSSIDList.dwDataLen); } // set the data in the Interface's context MemFree(pIntfContext->pwzcPList); pIntfContext->pwzcPList = pNewPList; // if this is not referring to the template object.. if (pIntfContext->wszGuid != NULL) { //..let 802.1X know about the change. (don't care about the return value) ElWZCCfgChangeHandler( pIntfContext->wszGuid, pIntfContext->pwzcPList); } dwOutFlags |= INTF_PREFLIST; dwLErr = ERROR_SUCCESS; } if (dwErr == ERROR_SUCCESS) dwErr = dwLErr; } // if there is anything more to set to this interface if (dwInFlags & ~(INTF_PREFLIST|INTF_ALL_FLAGS)) { // and the control flag INTFCTL_ENABLED doesn't allow this if (!(pIntfContext->dwCtlFlags & INTFCTL_ENABLED)) { // signal "request refused" error dwLErr = ERROR_REQUEST_REFUSED; } else { DWORD dwLOutFlags; // else go and set the oids dwLErr = DevioSetIntfOIDs( pIntfContext, pIntfEntry, dwInFlags, &dwLOutFlags); dwOutFlags |= dwLOutFlags; } if (dwErr == ERROR_SUCCESS) dwErr = dwLErr; } // log the user preference DbLogWzcInfo(WZCSVC_USR_CFGCHANGE, pIntfContext); // act on the changes.. dwLErr = LstActOnChanges(dwOutFlags, pIntfContext); if (dwErr == ERROR_SUCCESS) dwErr = dwLErr; LstRccsUnlockUnref(pIntfContext); exit: if (pdwOutFlags != NULL) *pdwOutFlags = dwOutFlags; DbgPrint((TRC_TRACK, "LstSetInterface]=%d", dwErr)); return dwErr; } //----------------------------------------------------------- // Checks whether interface changes should cause the interface to be // reinserted in the state machine and it does so if needed. // [in] dwChangedFlags indicates what the changes are. (see // bitmasks INTF_*) // [in] pIntfContext context of the interface being changed. DWORD LstActOnChanges( DWORD dwChangedFlags, PINTF_CONTEXT pIntfContext) { DWORD dwErr = ERROR_SUCCESS; DWORD dwLFlags = INTF_LIST_SCAN; BOOL bAltered = FALSE; DbgPrint((TRC_TRACK, "[LstActOnChanges(0x08x, %p)", dwChangedFlags, pIntfContext)); // if the changes involve the list of preferred networks or the control flags // then we should act on these changes.. if (dwChangedFlags & (INTF_PREFLIST|INTF_ALL_FLAGS)) { // if this is not the interface template object then just reset this interface if (pIntfContext->wszGuid != NULL) { // some interface changed, apply the template again on top of it if (g_wzcInternalCtxt.bValid) { PINTF_CONTEXT pIntfTContext; EnterCriticalSection(&g_wzcInternalCtxt.csContext); pIntfTContext = g_wzcInternalCtxt.pIntfTemplate; LstRccsReference(pIntfTContext); LeaveCriticalSection(&g_wzcInternalCtxt.csContext); LstRccsLock(pIntfTContext); dwErr = LstApplyTemplate( pIntfTContext, pIntfContext, &bAltered); LstRccsUnlockUnref(pIntfTContext); } // if any of the control flags or the static list changed, // these settings should go into the registry now StoSaveIntfConfig(NULL, pIntfContext); // since we're resetting the state machine, turn back on the "signal" flag pIntfContext->dwCtlFlags |= INTFCTL_INTERNAL_SIGNAL; dwErr = StateDispatchEvent( eEventCmdRefresh, pIntfContext, &dwLFlags); // clear up the INTFCTL_INTERNAL_BLK_MEDIACONN bit since this is not a media sense handler pIntfContext->dwCtlFlags &= ~INTFCTL_INTERNAL_BLK_MEDIACONN; } // if this is the interface template, then... else { PLIST_ENTRY pEntry; // since the template changed, save it to the registry here dwErr = StoSaveIntfConfig(NULL, pIntfContext); DbgAssert((dwErr == ERROR_SUCCESS, "Error %d while storing the template to registry")); // iterate through all the interfaces, apply the changes from the interface template // and reset each of them EnterCriticalSection(&g_lstIntfHashes.csMutex); for (pEntry = g_lstIntfHashes.lstIntfs.Flink; pEntry != &g_lstIntfHashes.lstIntfs; pEntry = pEntry->Flink) { PINTF_CONTEXT pIntfLContext = CONTAINING_RECORD(pEntry, INTF_CONTEXT, Link); LstRccsReference(pIntfLContext); LstRccsLock(pIntfLContext); // Merge the template settings into the interface's context dwErr = LstApplyTemplate( pIntfContext, pIntfLContext, NULL); DbgAssert((dwErr == ERROR_SUCCESS, "Error %d while applying template to interface %S", dwErr, pIntfLContext->wszGuid)); // if any of the control flags or the static list changed, // these settings should go into the registry now StoSaveIntfConfig(NULL, pIntfLContext); // since we're resetting the state machine, turn back on the "signal" flag pIntfLContext->dwCtlFlags |= INTFCTL_INTERNAL_SIGNAL; dwErr = StateDispatchEvent( eEventCmdRefresh, pIntfLContext, &dwLFlags); DbgAssert((dwErr == ERROR_SUCCESS, "Error %d while resetting interface %S", dwErr, pIntfLContext->wszGuid)); // clear up the INTFCTL_INTERNAL_BLK_MEDIACONN bit since this is not a media sense handler pIntfLContext->dwCtlFlags &= ~INTFCTL_INTERNAL_BLK_MEDIACONN; LstRccsUnlockUnref(pIntfLContext); } LeaveCriticalSection(&g_lstIntfHashes.csMutex); } } if (dwErr == ERROR_SUCCESS && bAltered) dwErr = ERROR_PARTIAL_COPY; DbgPrint((TRC_TRACK, "LstActOnChanges]=%d", dwErr)); return dwErr; } //----------------------------------------------------------- // Applies settings from the template context to the given interface context // [in] dwChanges: flags indicating settings that should be applied. // [in] pIntfTemplate: Interface template to pick settings from // [in] pIntfContext: Interface context to apply template to. DWORD LstApplyTemplate( PINTF_CONTEXT pIntfTemplate, PINTF_CONTEXT pIntfContext, LPBOOL pbAltered) { DWORD dwErr = ERROR_SUCCESS; PWZC_802_11_CONFIG_LIST pwzcTList = pIntfTemplate->pwzcPList; PWZC_802_11_CONFIG_LIST pwzcPList = pIntfContext->pwzcPList; PWZC_802_11_CONFIG_LIST pwzcRList = NULL; // resulting list ENUM_SELCATEG iCtg; UINT i, n, nCnt[7] = {0}; BOOL bAltered = FALSE; PWZC_WLAN_CONFIG pTHInfra = NULL, pTHAdhoc = NULL; // head of Infra/Adhoc groups in the template list PWZC_WLAN_CONFIG pPHInfra = NULL, pPHAdhoc = NULL; // head of Infra/Adhoc groups in the preferred list PWZC_WLAN_CONFIG pOneTime = NULL; // pointer to the "one time configuration" if any DbgPrint((TRC_TRACK,"[LstApplyTemplate(%p->%p)", pIntfTemplate, pIntfContext)); // apply the flags, if there are any provided if (pIntfTemplate->dwCtlFlags & INTF_POLICY) { DWORD dwPFlags = (pIntfContext->dwCtlFlags & INTF_ALL_FLAGS) & ~(INTF_OIDSSUPP); DWORD dwTFlags = (pIntfTemplate->dwCtlFlags & INTF_ALL_FLAGS) & ~(INTF_OIDSSUPP); if (dwPFlags != dwTFlags) { // if the policy flags are different from the interface's flags then // copy over just the "user" flag but don't overwrite the OIDSSUPP bit. dwPFlags = (pIntfContext->dwCtlFlags & ~INTF_ALL_FLAGS) | (pIntfTemplate->dwCtlFlags & INTF_ALL_FLAGS); if (pIntfContext->dwCtlFlags & INTF_OIDSSUPP) dwPFlags |= INTF_OIDSSUPP; else dwPFlags &= ~INTF_OIDSSUPP; pIntfContext->dwCtlFlags = dwPFlags; bAltered = TRUE; } } else { // currently policy could come only through the template. Consequently, // if the template is not policy, local setting should not be policy either. // Also, whatever the policy plumbs last, should be persisted. pIntfContext->dwCtlFlags &= ~(INTF_POLICY|INTF_VOLATILE); } // check the interface's list of preferred networks if (pwzcPList != NULL) { for (i = 0; i < pwzcPList->NumberOfItems; i++) { PWZC_WLAN_CONFIG pPConfig = &(pwzcPList->Config[i]); // keep a pointer to the "one time config" if there is one. if (i == pwzcPList->Index) pOneTime = pPConfig; // tag each entry in the preferred list with its respective category if (pPConfig->InfrastructureMode == Ndis802_11Infrastructure) { if (pPHInfra == NULL) pPHInfra = pPConfig; iCtg = ePI; } else if (pPConfig->InfrastructureMode == Ndis802_11IBSS) { if (pPHAdhoc == NULL) pPHAdhoc = pPConfig; iCtg = ePA; } else iCtg = eN; // regardless the above logic, exclude this configuration from the result list in // either of the two cases: // - the config is marked "shadow": that is, it is irrelevant without a matching template // configuration. // - the config is marked "volatile": that is, it has to go away unless the template is saying // otherwise. // This test needs to be done here and not earlier, because we do need pPHInfra and pPHAdhoc // to be set up correctly, taking into account all the configurations. if (pPConfig->dwCtlFlags & (WZCCTL_INTERNAL_SHADOW|WZCCTL_VOLATILE)) iCtg = eN; NWB_SET_SELCATEG(pPConfig, iCtg); nCnt[iCtg]++; } } // check the list of networks enforced by the template if (pwzcTList != NULL) { for (i = 0; i < pwzcTList->NumberOfItems; i++) { PWZC_WLAN_CONFIG pTConfig = &(pwzcTList->Config[i]); PWZC_WLAN_CONFIG pPConfig; if (pTConfig->InfrastructureMode == Ndis802_11Infrastructure) { if (pTHInfra == NULL) pTHInfra = pTConfig; iCtg = eVPI; } else if (pTConfig->InfrastructureMode == Ndis802_11IBSS) { if (pTHAdhoc == NULL) pTHAdhoc = pTConfig; iCtg = eVPA; } else { iCtg = eN; continue; } pPConfig = WzcFindConfig(pwzcPList, pTConfig, 0); // if there is an equivalent preference for the given template... if (pPConfig != NULL) { // if the template is policy, it should stomp over the preference if (pTConfig->dwCtlFlags & WZCCTL_POLICY) { BOOL bWepOnlyDiff; // if the configurations contents don't match ... if (!WzcMatchConfig(pTConfig, pPConfig, &bWepOnlyDiff)) { // even if the configs don't match, we need to pick up the // WEP key from the one provided by the user and mark the template // configuration as "shadow"-ed. pTConfig->KeyIndex = pPConfig->KeyIndex; pTConfig->KeyLength = pPConfig->KeyLength; // the key length has already been checked! DbgAssert((pTConfig->KeyLength <= WZCCTL_MAX_WEPK_MATERIAL, "WEP Key too large!!!")); memcpy(pTConfig->KeyMaterial, pPConfig->KeyMaterial, pTConfig->KeyLength); pTConfig->dwCtlFlags |= WZCCTL_INTERNAL_SHADOW; // signal the user illegally attempted to alter the policy if these // changes include more than just the WEP key. if (!bWepOnlyDiff) bAltered = TRUE; } // if the configurations do match check the order. else { // if the offsets of the template & preferred in their respective // groups are different, then it means the policy configuration have // been reordered - not allowed, hence set the "Altered" bit. if ((pTConfig->InfrastructureMode == Ndis802_11Infrastructure && (pTConfig - pTHInfra) != (pPConfig - pPHInfra) ) || (pTConfig->InfrastructureMode == Ndis802_11IBSS && (pTConfig - pTHAdhoc) != (pPConfig - pPHAdhoc) ) ) { bAltered = TRUE; } } // also, if the policy is substituting the "one time config", // make the "one time config" to become the policy config. if (pOneTime == pPConfig) pOneTime = pTConfig; // push the template NWB_SET_SELCATEG(pTConfig, iCtg); nCnt[iCtg]++; // and take out the conflicting preference iCtg = NWB_GET_SELCATEG(pPConfig); nCnt[iCtg]--; NWB_SET_SELCATEG(pPConfig, eN); nCnt[eN]++; } // this non-policy template and already has an equivalent preference. // take it out then else { iCtg = eN; NWB_SET_SELCATEG(pTConfig, iCtg); nCnt[iCtg]++; } } // there is no equivalent preference for the given template... else { // we don't have any preference for this template, so the template // is just pumped into the preference list. // if the template is a policy, it means the user deleted it and // he shouldn't have done so. Set the "Altered" bit then. if (pTConfig->dwCtlFlags & WZCCTL_POLICY) bAltered = TRUE; // just push the template no matter what. NWB_SET_SELCATEG(pTConfig, iCtg); nCnt[iCtg]++; } } } // calculate the number of entries in the resulting list n = 0; for (iCtg=eVPI; iCtg < eN; iCtg++) n += nCnt[iCtg]; // if there is not a single entry in the resulting list, // get out now. pwzcRList is already NULL. if (n == 0) goto exit; // ..allocate the new preferred list pwzcRList = (PWZC_802_11_CONFIG_LIST) MemCAlloc(FIELD_OFFSET(WZC_802_11_CONFIG_LIST, Config) + n * sizeof(WZC_WLAN_CONFIG)); if (pwzcRList == NULL) { dwErr = GetLastError(); goto exit; } // list is successfully allocated pwzcRList->NumberOfItems = n; pwzcRList->Index = n; // now change the semantic of all counters to mean "indices in the selection list" // for their respective group of entries for (iCtg = eN-1; iCtg >= eVPI; iCtg--) { n -= nCnt[iCtg]; nCnt[iCtg] = n; } // copy over in the new list the entries enforced by the template if (pwzcTList != NULL) { for (i = 0; i < pwzcTList->NumberOfItems; i++) { PWZC_WLAN_CONFIG pTConfig = &(pwzcTList->Config[i]); iCtg = NWB_GET_SELCATEG(pTConfig); if (iCtg != eN) { PWZC_WLAN_CONFIG pRConfig = &(pwzcRList->Config[nCnt[iCtg]]); // copy the whole template configuration to the result list memcpy(pRConfig, pTConfig, sizeof(WZC_WLAN_CONFIG)); // just for making sure, reset the 'deleted' flag as this is a brand new // config that was never attempted. pRConfig->dwCtlFlags &= ~WZCCTL_INTERNAL_DELETED; // if the template configuration is marked as being shadowed.. if (pTConfig->dwCtlFlags & WZCCTL_INTERNAL_SHADOW) { // ..set it to the preferred configuration also.. pRConfig->dwCtlFlags |= WZCCTL_INTERNAL_SHADOW; // ..and make sure the preferred configuration gets persisted // since it contains user information.. pRConfig->dwCtlFlags &= ~WZCCTL_VOLATILE; // leave the template configuration with the bit "shadow" since // it has been altered. This way, if the template is subsequently // "applied" the user config won't get the "volatile" bit set back // and it will still be marked "shadow". } // if this is the "one time" config, adjust the Index to point to this entry's index if (pOneTime == pTConfig) pwzcRList->Index = nCnt[iCtg]; nCnt[iCtg]++; } // reset the selection category we have used for this resulting entry NWB_SET_SELCATEG(pTConfig, 0); } } // copy over in the new list the entries from the original list if (pwzcPList != NULL) { for (i = 0; i < pwzcPList->NumberOfItems; i++) { PWZC_WLAN_CONFIG pPConfig = &(pwzcPList->Config[i]); iCtg = NWB_GET_SELCATEG(pPConfig); if (iCtg != eN) { PWZC_WLAN_CONFIG pRConfig = &(pwzcRList->Config[nCnt[iCtg]]); // copy the whole preferred configuration to the result list memcpy(pRConfig, pPConfig, sizeof(WZC_WLAN_CONFIG)); // just for making sure, reset the 'deleted' flag as this is a brand new // config that was never attempted. pRConfig->dwCtlFlags &= ~WZCCTL_INTERNAL_DELETED; // if this is the "one time" config, adjust the Index to point to this entry's index if (pOneTime == pPConfig) pwzcRList->Index = nCnt[iCtg]; nCnt[iCtg]++; } // reset the selection category we have used for this preferred entry NWB_SET_SELCATEG(pPConfig, 0); } } exit: if (dwErr == ERROR_SUCCESS) { // cleanup the original list and put in the new one WzcCleanupWzcList(pIntfContext->pwzcPList); pIntfContext->pwzcPList = pwzcRList; } if (pbAltered != NULL) *pbAltered = bAltered; DbgPrint((TRC_TRACK,"LstApplyTemplate]=%d", dwErr)); return dwErr; } //----------------------------------------------------------- // Refreshes the specified parameters on the specified adapter. // [in] dwInFlags specifies the parameters to be set. (see // bitmasks INTF_* and INTF_RFSH_*) // [in] pIntfEntry should contain the GUID of the adapter // [out] pdwOutFlags provides an indication on the params that // were successfully refreshed to the adapter // Each parameter for which the driver says that was refreshed // successfully is copied into the interface's context. DWORD LstRefreshInterface( DWORD dwInFlags, PINTF_ENTRY pIntfEntry, LPDWORD pdwOutFlags) { DWORD dwErr, dwLErr; PHASH_NODE pNode = NULL; DWORD dwOutFlags = 0; PINTF_CONTEXT pIntfContext; DbgPrint((TRC_TRACK, "[LstRefreshInterface")); if (g_lstIntfHashes.bValid) { EnterCriticalSection(&g_lstIntfHashes.csMutex); dwErr = HshQueryObjectRef( g_lstIntfHashes.pHnGUID, pIntfEntry->wszGuid, &pNode); if (dwErr == ERROR_SUCCESS) { pIntfContext = pNode->pObject; LstRccsReference(pIntfContext); } LeaveCriticalSection(&g_lstIntfHashes.csMutex); } else dwErr = ERROR_ARENA_TRASHED; // the interface needs to exist in order to refresh it if (dwErr == ERROR_SUCCESS) { LstRccsLock(pIntfContext); // if description is requested to be refreshed, do it now if (dwInFlags & INTF_DESCR) { CHAR QueryBuffer[QUERY_BUFFER_SIZE]; PNDISUIO_QUERY_BINDING pBinding; RAW_DATA rdBuffer; // get first the binding structure for this interface rdBuffer.dwDataLen = sizeof(QueryBuffer); rdBuffer.pData = QueryBuffer; pBinding = (PNDISUIO_QUERY_BINDING)rdBuffer.pData; dwLErr = DevioGetInterfaceBindingByGuid( INVALID_HANDLE_VALUE, // the call will open Ndisuio locally pIntfContext->wszGuid, // interface GUID as "{guid}" &rdBuffer); // regardless of success, lets clean the current description MemFree(pIntfContext->wszDescr); pIntfContext->wszDescr = NULL; // if everything went fine if (dwLErr == ERROR_SUCCESS) { LPWSTR wszName; DWORD dwNameLen; // Copy the interface's description.name wszName = (LPWSTR)((LPBYTE)pBinding + pBinding->DeviceDescrOffset); // the DeviceDescrLength is in bytes and includes the null terminator dwNameLen = pBinding->DeviceDescrLength; if (dwNameLen > 0) { pIntfContext->wszDescr = MemCAlloc(dwNameLen); if (pIntfContext->wszGuid == NULL) dwErr = GetLastError(); else wcscpy(pIntfContext->wszDescr, wszName); } } // if all went fine, mark it out if (dwLErr == ERROR_SUCCESS) dwOutFlags |= INTF_DESCR; if (dwErr == ERROR_SUCCESS) dwErr = dwLErr; } // refresh any ndis settings if requested if (dwInFlags & INTF_NDISMEDIA) { dwLErr = DevioGetIntfStats(pIntfContext); if (dwLErr == ERROR_SUCCESS) dwOutFlags |= INTF_NDISMEDIA; if (dwErr == ERROR_SUCCESS) dwErr = dwLErr; } if (dwInFlags & INTF_ALL_OIDS) { DWORD dwLFlags = dwInFlags; // feed the state machine with the "refresh command" for this context dwLErr = StateDispatchEvent(eEventCmdRefresh, pIntfContext, &dwLFlags); // clear up the INTFCTL_INTERNAL_BLK_MEDIACONN bit since this is not a media sense handler pIntfContext->dwCtlFlags &= ~INTFCTL_INTERNAL_BLK_MEDIACONN; if (dwLErr == ERROR_SUCCESS) dwOutFlags |= dwLFlags; if (dwErr == ERROR_SUCCESS) dwErr = dwLErr; } LstRccsUnlockUnref(pIntfContext); } if (pdwOutFlags != NULL) *pdwOutFlags = dwOutFlags; DbgPrint((TRC_TRACK, "LstRefreshInterface]=%d", dwErr)); return dwErr; } //----------------------------------------------------------- // Builds the list of configurations to be tried from the list of visible // configurations, the list of preferred configurations and based on the // interface's mode (Auto/Infra/Adhoc) and flags (is the service enabled?, // fallback to visible?). // [in] pIntfContext: Interface for which is done the selection // [out] ppwzcSList: pointer to the list of selected configurations //----------------------------------------------------------- // Builds the list of configurations to be tried from the list of visible // configurations, the list of preferred configurations and based on the // interface's mode (Auto/Infra/Adhoc) and flags (is the service enabled?, // fallback to visible?). // [in] pIntfContext: Interface for which is done the selection // [out] ppwzcSList: pointer to the list of selected configurations DWORD LstBuildSelectList( PINTF_CONTEXT pIntfContext, PWZC_802_11_CONFIG_LIST *ppwzcSList) { DWORD dwErr = ERROR_SUCCESS; UINT i, n; UINT nCnt[7] = {0}; ENUM_SELCATEG iVCtg, iPCtg, iCtg; PWZC_WLAN_CONFIG pCrtSConfig = NULL; DbgPrint((TRC_TRACK,"[LstBuildSelectList(0x%p)", pIntfContext)); DbgAssert((pIntfContext != NULL, "(null) Interface context in LstBuildSelectList")); DbgAssert((ppwzcSList != NULL, "invalid (null) out param")); // set the pointer to the selection list to NULL (*ppwzcSList) = NULL; // for each entry in the visible list (if any), if the entry is probable to be included // in the selection set (either as Visible Infra or Visible Adhoc) set the category // attribute to point to the corresponding set and ++ the corresponding counter. // if the entry is not going to be included, set the same byte to eN (neutral) if (pIntfContext->pwzcVList) { for (i=0; i < pIntfContext->pwzcVList->NumberOfItems; i++) { PWZC_WLAN_CONFIG pVConfig = &(pIntfContext->pwzcVList->Config[i]); // the visible list might contain several APs for the same network. // Make sure we exclude the duplicates from the selection list. PWZC_WLAN_CONFIG pVDup = WzcFindConfig(pIntfContext->pwzcVList, pVConfig, i+1); // don't even consider this visible network if: // - another duplicate exists further in the list, or // - "automatically connect to non-preferred network" is not selected or // - its network type (infra / ad hoc) doesn't match the type selected in the intf config or // - the entry is blocked in the "blocked configurations" list (pwzcBList) if ((pVDup != NULL) || !(pIntfContext->dwCtlFlags & INTFCTL_FALLBACK) || (((pIntfContext->dwCtlFlags & INTFCTL_CM_MASK) != Ndis802_11AutoUnknown) && ((pIntfContext->dwCtlFlags & INTFCTL_CM_MASK) != pVConfig->InfrastructureMode)) || WzcFindConfig(pIntfContext->pwzcBList, pVConfig, 0) != NULL) iVCtg = eN; else if (pVConfig->InfrastructureMode == Ndis802_11Infrastructure) iVCtg = eVI; else if (pVConfig->InfrastructureMode == Ndis802_11IBSS) iVCtg = eVA; else iVCtg = eN; NWB_SET_SELCATEG(pVConfig, iVCtg); nCnt[iVCtg]++; } } // Locate the current successful configuration (if any) in the preferred list. This config // should be marked Visible, regardless it is or not present in the visible list. if (pIntfContext->pwzcSList != NULL && pIntfContext->pwzcSList->Index < pIntfContext->pwzcSList->NumberOfItems) { PWZC_WLAN_CONFIG pCrtConfig; pCrtConfig = &(pIntfContext->pwzcSList->Config[pIntfContext->pwzcSList->Index]); if (!(pCrtConfig->dwCtlFlags & WZCCTL_INTERNAL_DELETED)) pCrtSConfig = WzcFindConfig(pIntfContext->pwzcPList, pCrtConfig, 0); } // for each entry in the preferred list (if any), if the entry matches the interface's mode // and is a "visible" one, put it in either eVPI or eVPA category and pull out the corresponding // visible entry from eVI or eVA or eN (if that entry was not supposed to be included in the selection). // If the entry is not "visible" put it either in ePI (only if the interface doesn't fallback to // visible) or in ePA category. if (pIntfContext->pwzcPList != NULL) { for (i=0; i < pIntfContext->pwzcPList->NumberOfItems; i++) { PWZC_WLAN_CONFIG pPConfig = &(pIntfContext->pwzcPList->Config[i]); // don't even consider this preferred network if: // - its network type (infra / ad hoc) doesn't match the type selected in the intf config or // - the entry is blocked in the "blocked configurations" list (pwzcBList) if ((((pIntfContext->dwCtlFlags & INTFCTL_CM_MASK) != Ndis802_11AutoUnknown) && ((pIntfContext->dwCtlFlags & INTFCTL_CM_MASK) != pPConfig->InfrastructureMode)) || WzcFindConfig(pIntfContext->pwzcBList, pPConfig, 0) != NULL) { iPCtg = eN; } else { PWZC_WLAN_CONFIG pVConfig = WzcFindConfig(pIntfContext->pwzcVList, pPConfig, 0); if (pPConfig == pCrtSConfig || pVConfig != NULL) { // this preferred entry is either the current one or visible, // so point it either to eVPI or eVPA if (pPConfig->InfrastructureMode == Ndis802_11Infrastructure) iPCtg = eVPI; else if (pPConfig->InfrastructureMode == Ndis802_11IBSS) iPCtg = eVPA; else iPCtg = eN; if (pVConfig != NULL) { // the corresponding visible entry (if any) has to be pulled out from whichever // category it was in and has to be put in eN (neutral) nCnt[NWB_GET_SELCATEG(pVConfig)]--; iVCtg = eN; NWB_SET_SELCATEG(pVConfig, iVCtg); nCnt[iVCtg]++; } else { DbgPrint((TRC_TRACK, "Non-visible crt config raised to visible categ %d.", iPCtg)); } } else { // this preferred entry is not visible, so either point it to ePI or to ePA. if (pPConfig->InfrastructureMode == Ndis802_11Infrastructure) iPCtg = ePI; else if (pPConfig->InfrastructureMode == Ndis802_11IBSS) iPCtg = ePA; else iPCtg = eN; } } NWB_SET_SELCATEG(pPConfig, iPCtg); nCnt[iPCtg]++; } } // calculate the number of entries in the selection list n = 0; for (iCtg=eVPI; iCtg < eN; iCtg++) n += nCnt[iCtg]; // if there are any entries to copy afterall.. if (n != 0) { // ..allocate the selection list (*ppwzcSList) = (PWZC_802_11_CONFIG_LIST) MemCAlloc(FIELD_OFFSET(WZC_802_11_CONFIG_LIST, Config) + n * sizeof(WZC_WLAN_CONFIG)); if ((*ppwzcSList) == NULL) { dwErr = GetLastError(); goto exit; } (*ppwzcSList)->NumberOfItems = n; (*ppwzcSList)->Index = n; // now change the semantic of all counters to mean "indices in the selection list" // for their respective group of entries for (iCtg = eN-1; iCtg >= eVPI; iCtg--) { n -= nCnt[iCtg]; nCnt[iCtg] = n; } } exit: // copy first the entries from the preferred list into the selection list, // at the index corresponding to their categories. if (pIntfContext->pwzcPList != NULL) { for (i=0; i < pIntfContext->pwzcPList->NumberOfItems; i++) { PWZC_WLAN_CONFIG pPConfig = &(pIntfContext->pwzcPList->Config[i]); // get the category for this preferred configuration iCtg = NWB_GET_SELCATEG(pPConfig); // if we have a selection list to copy into, and the entry is supposed // to be copied (not neutral) do it here if ((*ppwzcSList) != NULL && iCtg != eN) { PWZC_WLAN_CONFIG pSConfig = &((*ppwzcSList)->Config[nCnt[iCtg]]); // copy the whole preferred configuration (including the selection category // & authentication mode) to the selection list memcpy(pSConfig, pPConfig, sizeof(WZC_WLAN_CONFIG)); // just for making sure, reset the 'deleted' flag as this is a brand new // config that was never attempted. pSConfig->dwCtlFlags &= ~WZCCTL_INTERNAL_DELETED; // the remaining attributes (selection category, authentication mode) should be // left untouched. // make sure we propagate the 'start from index' if the preferred list shows like // a one time connect is requested if (i == pIntfContext->pwzcPList->Index) (*ppwzcSList)->Index = nCnt[iCtg]; nCnt[iCtg]++; } // reset the selection category we have used for this preferred entry NWB_SET_SELCATEG(pPConfig, 0); } } // next, copy the entries from the visible list into the selection list, // at the index corresponding to their categories. if (pIntfContext->pwzcVList != NULL) { for (i=0; i < pIntfContext->pwzcVList->NumberOfItems; i++) { PWZC_WLAN_CONFIG pVConfig = &(pIntfContext->pwzcVList->Config[i]); iCtg = NWB_GET_SELCATEG(pVConfig); // if we have a selection list to copy into, and the entry is supposed // to be copied (not neutral) do it here if ((*ppwzcSList) != NULL && iCtg != eN) { PWZC_WLAN_CONFIG pSConfig = &((*ppwzcSList)->Config[nCnt[iCtg]]); // copy the whole visible configuration (including its selection category) // to the selection set (for visible entries, authentication mode is 0 by default // since this information is not provided by the nic/driver) memcpy(pSConfig, pVConfig, sizeof(WZC_WLAN_CONFIG)); // just for making sure, reset the 'deleted' flag as this is a brand new // config that was never attempted. pSConfig->dwCtlFlags &= ~WZCCTL_INTERNAL_DELETED; // bump up the index for this entry's category nCnt[iCtg]++; } // reset the selection category we have used for this visible entry NWB_SET_SELCATEG(pVConfig, 0); } } DbgPrint((TRC_TRACK,"LstBuildSelectList]=%d", dwErr)); return dwErr; } //----------------------------------------------------------- // Checks whether the list of selected configurations has changed such // that it is required to replumb the selection. // [in] pIntfContext: Interface for which is done the selection // [in] pwzcSList: new selection list to check the configuration against // [out] pnSelIdx: if selection changed, provides the index where to start iterate from // Returns: TRUE if replumbing is required. In this case, pnSelIdx is // set to the configuration to start iterate from. BOOL LstChangedSelectList( PINTF_CONTEXT pIntfContext, PWZC_802_11_CONFIG_LIST pwzcNSList, LPUINT pnSelIdx) { BOOL bRet = FALSE; DbgPrint((TRC_TRACK,"[LstChangedSelectList(0x%p)", pIntfContext)); DbgAssert((pnSelIdx != NULL,"invalid (null) pointer to out param")); // before anything, zero out the index for the current selected network *pnSelIdx = 0; // if there is no configuration the current selected list, // it means either we're just initializing the context or we failed all the // other configs. Either way, we need to replumb the card according to the // new selection list (if that one is also empty, we will follow the path // {SQ}->{SIter}->{SF}) if (pIntfContext->pwzcSList == NULL || pIntfContext->pwzcSList->NumberOfItems == 0 || pwzcNSList == NULL || pwzcNSList->NumberOfItems == 0) { DbgPrint((TRC_STATE, "SelList changed? YES; the current/new selection list is empty")); // since we're starting fresh, make sure to propagate the "one time connect" index, if any if (pwzcNSList != NULL && pwzcNSList->Index < pwzcNSList->NumberOfItems) *pnSelIdx = pwzcNSList->Index; bRet = TRUE; } else // we do have an old SList we need to look at { PWZC_WLAN_CONFIG pSConfig; // current sucessful configuration PWZC_WLAN_CONFIG pNSConfig; // successful configuration in the new selection list DbgAssert((pIntfContext->pwzcSList->Index < pIntfContext->pwzcSList->NumberOfItems, "Selection index %d points outside SList[0..%d]", pIntfContext->pwzcSList->Index, pIntfContext->pwzcSList->NumberOfItems)); // get a pointer to the current successful configuration pSConfig = &(pIntfContext->pwzcSList->Config[pIntfContext->pwzcSList->Index]); // as the first thing, let's check for the "one time connect". If this is requested, // the one time config has to match with the current selected config. Otherwise this // is a change already. if (pwzcNSList->Index < pwzcNSList->NumberOfItems) { DbgPrint((TRC_STATE, "SList changed? Yes, \"one time connect\" is requested.")); // in this case, we do mark this as a "change". Otherwise it is difficult to keep // the association to the "one time" connect for more than a scan cycle (other changes // will take precedence to the next round, when "one time" connect flag won't be there // anymore *pnSelIdx = pwzcNSList->Index; bRet = TRUE; } // if it is not decided yet whether it is a change (that is this is not a "one time connect")... if (!bRet) { // search for the crt successful config into the new selection list. pNSConfig = WzcFindConfig(pwzcNSList, pSConfig, 0); if (pNSConfig == NULL) { UINT i; ENUM_SELCATEG iSCtg; DbgPrint((TRC_STATE, "SList changed? Don't know yet. The current config is not in the NSList")); // the crt successful config is not in the new selection list. If the crt selection is // marked as being of the "preferred" kind, there is no other way it could disappear // other than being explicitly removed from the preferred list. In this case, yes, // this is a change. iSCtg = NWB_GET_SELCATEG(pSConfig); if (iSCtg != eVI && iSCtg != eVA) { DbgPrint((TRC_STATE, "SList changed? Yes. The current preferred network has been removed.")); bRet = TRUE; *pnSelIdx = 0; // iterate from the very beginning. } // In all the remaining cases (VI or VA), we need to check each of the new selected configurations // if it doesn't prevail the current successful one. for (i = 0; !bRet && i < pwzcNSList->NumberOfItems; i++) { PWZC_WLAN_CONFIG pNConfig, pConfig; // get the new configuration and search it into the current selection list pNConfig = &(pwzcNSList->Config[i]); pConfig = WzcFindConfig(pIntfContext->pwzcSList, pNConfig, 0); // if the new selected configuration was not tried before either because // it just showed up or because we didn't get to try it previously, // then it is a potential better candidate if (pConfig == NULL || pSConfig < pConfig) { ENUM_SELCATEG iNSCtg; // get the category for the new config iNSCtg = NWB_GET_SELCATEG(pNConfig); // if the new configuration has a prevailing category, we should // definitely replumb starting from here if (iNSCtg < iSCtg) { DbgPrint((TRC_STATE,"SList changed? YES; a config with a better category has been detected.")); bRet = TRUE; *pnSelIdx = i; } // remember: here, the current selected config can only be VI or VA. That is, if the category of // any newcomer in the selection list is even equal or greater that the current category, there // is absolutely no point in moving out of here. } // there is a matching config which we tried before. We do acknowledge // a change if the the two configs actually don't have matching content! else if (!WzcMatchConfig(pNConfig, pConfig, NULL)) { DbgPrint((TRC_STATE,"SList changed? YES; a better config failed before but it has been altered.")); bRet = TRUE; *pnSelIdx = i; } } } else // the current selected network is still in the new selection list (pNSConfig) { UINT i; // for each config in the new selection list, try to match it with an existent // configuration in the crt selection list for (i = 0; !bRet && i < pwzcNSList->NumberOfItems; i++) { PWZC_WLAN_CONFIG pNConfig, pConfig; ENUM_SELCATEG iNSCtg, iSCtg; // if we are already at the current successful configuration this means // we didn't find any new config to justify replumbing the interface pNConfig = &(pwzcNSList->Config[i]); if (pNConfig == pNSConfig) { bRet = !WzcMatchConfig(pNConfig, pSConfig, NULL); if (bRet) DbgPrint((TRC_STATE,"SList changed? YES; no better config found, but the current one has been altered.")); else DbgPrint((TRC_STATE, "SList changed? NO; there is no new config that was not tried yet")); break; } // get the category for the config in the new list iNSCtg = NWB_GET_SELCATEG(pNConfig); // search the configuration from the new selection list into the old selection list pConfig = WzcFindConfig(pIntfContext->pwzcSList, pNConfig, 0); // if this is either a brand new config, or one that has // been raised in front of the current selection... if (pConfig == NULL || pSConfig < pConfig) { // ...if the category is different, or is the same as the one of the successful config // but is of a "preferred" kind, then it means the list has changed. if (iNSCtg != NWB_GET_SELCATEG(pNSConfig) || (iNSCtg != eVI && iNSCtg != eVA)) { DbgPrint((TRC_STATE,"SList changed? YES: there is a new config of a different or preferred category")); bRet = TRUE; *pnSelIdx = i; } } else { // there is a matching entry in the old selection list, in front of the current // successful configuration. This means the configuration has been tried before and // failed. However, it could happen that the configuration was tried when it was // not visible and now it is visible. In such a case, we should attempt replumbing iSCtg = NWB_GET_SELCATEG(pConfig); if (iNSCtg != iSCtg && (iSCtg == ePI || iSCtg == ePA)) { DbgPrint((TRC_STATE,"SList changed? YES: a better config failed before but its categ changed from %d to %d", iSCtg, iNSCtg)); bRet = TRUE; *pnSelIdx = i; } else if (!WzcMatchConfig(pNConfig, pConfig, NULL)) { DbgPrint((TRC_STATE,"SList changed? YES; a better config failed before but it has been altered.")); bRet = TRUE; *pnSelIdx = i; } } } } } } DbgPrint((TRC_TRACK,"LstChangedSelectList]=%s", bRet ? "TRUE" : "FALSE")); return bRet; } // fake WEP key to be used if there is no key set (and obviously web = disabled) // and the remote guy requires privacy. This is a 104bit key. BYTE g_chFakeKeyMaterial[] = {0x56, 0x09, 0x08, 0x98, 0x4D, 0x08, 0x11, 0x66, 0x42, 0x03, 0x01, 0x67, 0x66}; //----------------------------------------------------------- // Plumbs the interface with the selected configuration as it is pointed // out by pwzcSList fields in the pIntfContext. Optional, it can // return in ppSelSSID the configuration that was plumbed down // [in] pIntfContext: Interface context identifying ctl flags & the selected SSID // [out] ppndSelSSID: pointer to the SSID that is being plumbed down. DWORD LstSetSelectedConfig( PINTF_CONTEXT pIntfContext, PWZC_WLAN_CONFIG *ppndSelSSID) { DWORD dwErr = ERROR_SUCCESS; PWZC_WLAN_CONFIG pSConfig; INTF_ENTRY IntfEntry = {0}; DWORD dwInFlags, dwOutFlags; BYTE chBuffer[sizeof(NDIS_802_11_WEP) + WZCCTL_MAX_WEPK_MATERIAL - 1]; BOOL bFakeWKey = FALSE; // flag indicating whether the fake WEP key is needed DbgPrint((TRC_TRACK, "[LstSetSelectedConfig(0x%p..)", pIntfContext)); DbgAssert((pIntfContext != NULL, "(null) interface context in LstSetSelectedConfig")); if (pIntfContext->pwzcSList == NULL || pIntfContext->pwzcSList->Index >= pIntfContext->pwzcSList->NumberOfItems) { dwErr = ERROR_INVALID_PARAMETER; goto exit; } // get a pointer to the configuration to set down to the card. pSConfig = &(pIntfContext->pwzcSList->Config[pIntfContext->pwzcSList->Index]); // build the IntfEntry object that will specify what exactly goes down to the card dwInFlags = INTF_AUTHMODE | INTF_INFRAMODE | INTF_SSID; // authentication mode //IntfEntry.nAuthMode = NWB_GET_AUTHMODE(pSConfig); IntfEntry.nAuthMode = pSConfig->AuthenticationMode; // infrastructure mode IntfEntry.nInfraMode = pSConfig->InfrastructureMode; // SSID IntfEntry.rdSSID.dwDataLen = pSConfig->Ssid.SsidLength; IntfEntry.rdSSID.pData = pSConfig->Ssid.Ssid; // if the configuration to be plumbed down requires the presence of a WEP Key... if (pSConfig->Privacy || IntfEntry.nAuthMode != Ndis802_11AuthModeOpen) { // if there is a WEP key provided in this configuration plumb it down if (pSConfig->dwCtlFlags & WZCCTL_WEPK_PRESENT) { PNDIS_802_11_WEP pndUserWKey = (PNDIS_802_11_WEP)chBuffer; // build the ndis WEP key structure from the user's key // the key is a "transmit" key, regardless the index pndUserWKey->KeyIndex = 0x80000000 | pSConfig->KeyIndex; pndUserWKey->KeyLength = pSConfig->KeyLength; memcpy(pndUserWKey->KeyMaterial, pSConfig->KeyMaterial, WZCCTL_MAX_WEPK_MATERIAL); pndUserWKey->Length = sizeof(NDIS_802_11_WEP) + pndUserWKey->KeyLength - 1; // TODO: here is where we should decrypt inplace the WEP key { UINT i; for (i = 0; i < WZCCTL_MAX_WEPK_MATERIAL; i++) pndUserWKey->KeyMaterial[i] ^= g_chFakeKeyMaterial[(7*i)%13]; } // and ask for it to be set down IntfEntry.rdCtrlData.dwDataLen = pndUserWKey->Length; IntfEntry.rdCtrlData.pData = (LPBYTE)pndUserWKey; dwInFlags |= INTF_ADDWEPKEY; DbgPrint((TRC_GENERIC,"Plumbing down the User WEP txKey [idx:%d,len:%d]", pSConfig->KeyIndex, pSConfig->KeyLength)); } // if a WEP Key is needed but none is provided for this configuration... else { // ...first thing to do is to ask the driver to reload its defaults. dwErr = DevioSetEnumOID( pIntfContext->hIntf, OID_802_11_RELOAD_DEFAULTS, (DWORD)Ndis802_11ReloadWEPKeys); DbgAssert((dwErr == ERROR_SUCCESS, "Failed setting OID_802_11_RELOAD_DEFAULTS")); // need to check if reloading the defaults fixed the issue (not having a key) dwErr = DevioRefreshIntfOIDs( pIntfContext, INTF_WEPSTATUS, NULL); DbgAssert((dwErr == ERROR_SUCCESS, "Failed refreshing OID_802_11_WEP_STATUS")); // if even after reloading the defaults, a key is still absent, then // set down the hardcoded key. if (dwErr == ERROR_SUCCESS && pIntfContext->wzcCurrent.Privacy == Ndis802_11WEPKeyAbsent) { PNDIS_802_11_WEP pndFakeWKey = (PNDIS_802_11_WEP)chBuffer; // we should set the hardcoded WEP key pndFakeWKey->KeyIndex = 0x80000000; pndFakeWKey->KeyLength = 5; // the fake key has to be the smallest possible (40bit) dwErr = WzcRndGenBuffer(pndFakeWKey->KeyMaterial, pndFakeWKey->KeyLength, 0, 255); DbgAssert((dwErr == ERROR_SUCCESS, "Failed to generate the random fake wep key")); pndFakeWKey->Length = sizeof(NDIS_802_11_WEP) + pndFakeWKey->KeyLength - 1; // and ask for it to be set down IntfEntry.rdCtrlData.dwDataLen = pndFakeWKey->Length; IntfEntry.rdCtrlData.pData = (LPBYTE)pndFakeWKey; dwInFlags |= INTF_ADDWEPKEY; bFakeWKey = TRUE; DbgPrint((TRC_GENERIC,"Plumbing down the Fake WEP txKey [len:%d]", IntfEntry.rdCtrlData.dwDataLen)); } } // now enable WEP only if privacy is required and the current settings // show the WEP is not enabled if (pSConfig->Privacy && pIntfContext->wzcCurrent.Privacy != Ndis802_11WEPEnabled) { // and also we should enable WEP if it shows as not being // already enabled IntfEntry.nWepStatus = Ndis802_11WEPEnabled; dwInFlags |= INTF_WEPSTATUS; } } // if the configuration to be plumbed doesn't require privacy but currently // WEP is enabled, disable it. if (!pSConfig->Privacy && pIntfContext->wzcCurrent.Privacy == Ndis802_11WEPEnabled) { IntfEntry.nWepStatus = Ndis802_11WEPDisabled; dwInFlags |= INTF_WEPSTATUS; } // if everything is fine so far... if (dwErr == ERROR_SUCCESS) { // ...go and plumb the card with the settings below dwErr = DevioSetIntfOIDs( pIntfContext, &IntfEntry, dwInFlags, &dwOutFlags); // if we attempted to change the WEP Key... if (dwInFlags & INTF_ADDWEPKEY) { //.. and the operation succeeded.. if (dwOutFlags & INTF_ADDWEPKEY) { // then either set the "fake key" flag - if it was a fake key.. if (bFakeWKey) pIntfContext->dwCtlFlags |= INTFCTL_INTERNAL_FAKE_WKEY; //..or reset it if we put a "real" key else pIntfContext->dwCtlFlags &= ~INTFCTL_INTERNAL_FAKE_WKEY; } // if plumbing down the key failed, leave the "fake key" flag as // it is since there were no changes made. } // ...or if we didn't need to plumb a WEP key, reset the flag else { pIntfContext->dwCtlFlags &= ~INTFCTL_INTERNAL_FAKE_WKEY; } } if (dwErr != ERROR_SUCCESS) DbLogWzcError(WZCSVC_ERR_CFG_PLUMB, pIntfContext, DbLogFmtSSID(0, &(pSConfig->Ssid)), dwErr); exit: DbgPrint((TRC_TRACK, "LstSetSelectedConfig]=%d", dwErr)); return dwErr; } //----------------------------------------------------------- // PnP notification handler // [in/out] ppIntfContext: Pointer to the Interface context for which // the notification was received // [in] dwNotifCode: Notification code (WZCNOTIF_*) // [in] wszDeviceKey: Key info on the device for which the notification // was received DWORD LstNotificationHandler( PINTF_CONTEXT *ppIntfContext, DWORD dwNotifCode, LPWSTR wszDeviceKey) { DWORD dwErr = ERROR_SUCCESS; PINTF_CONTEXT pIntfContext = *ppIntfContext; DbgPrint((TRC_TRACK,"[LstNotificationHandler(0x%p, %d, %S)", pIntfContext, dwNotifCode, wszDeviceKey)); if ((dwNotifCode == WZCNOTIF_DEVICE_ARRIVAL || dwNotifCode == WZCNOTIF_ADAPTER_BIND) && pIntfContext == NULL) { CHAR QueryBuffer[QUERY_BUFFER_SIZE]; PNDISUIO_QUERY_BINDING pQueryBinding; RAW_DATA rdBuffer; // get first the binding structure for this interface rdBuffer.dwDataLen = sizeof(QueryBuffer); rdBuffer.pData = QueryBuffer; pQueryBinding = (PNDISUIO_QUERY_BINDING)rdBuffer.pData; dwErr = DevioGetInterfaceBindingByGuid( INVALID_HANDLE_VALUE, // the call will open Ndisuio locally wszDeviceKey, // interface GUID as "{guid}" &rdBuffer); // if everything went fine if (dwErr != ERROR_SUCCESS) goto exit; // go build the INTF_CONTEXT structure, based on // the binding information (key info for the adapter) dwErr = LstConstructIntfContext( pQueryBinding, &pIntfContext); if (dwErr == ERROR_SUCCESS) { // increase its ref count and lock it up here LstRccsReference(pIntfContext); LstRccsLock(pIntfContext); // add it to the hashes dwErr = LstAddIntfToHashes(pIntfContext); if (dwErr == ERROR_SUCCESS) { dwErr = StateDispatchEvent( eEventAdd, pIntfContext, NULL); } // if for any reason hashing or dispatching failed, cleanup the context here if (dwErr != ERROR_SUCCESS) LstRemoveIntfContext(pIntfContext); // release the context here LstRccsUnlockUnref(pIntfContext); } // it could happen that a context was created but it turned out to be a non-wireless // adapter. In this case all memory has been freed up, but pIntfContext remained // non-null. We need to set this pointer back to null as it will be passed up. if (dwErr != ERROR_SUCCESS) pIntfContext = NULL; } // either for arrival or removal, we attempt to remove any identical context // If it is about an arrival, we shouldn't have any duplicate but who knows if ((dwNotifCode == WZCNOTIF_DEVICE_REMOVAL || dwNotifCode == WZCNOTIF_ADAPTER_UNBIND) && pIntfContext != NULL) { // increase its ref count and lock it up here LstRccsReference(pIntfContext); LstRccsLock(pIntfContext); DbLogWzcInfo(WZCSVC_EVENT_REMOVE, pIntfContext, pIntfContext->wszDescr); // save the interface's settings to the registry dwErr = StoSaveIntfConfig(NULL, pIntfContext); DbgAssert((dwErr == ERROR_SUCCESS, "StoSaveIntfConfig failed for Intf context 0x%p", pIntfContext)); // prepare this context for destruction LstRemoveIntfContext(pIntfContext); // at this point, there are no other timer routines that are going to be fired. Whatever // has been already fired ++ed the reference counter already so there is no risk to delete // the data prematurely (when unref-ing this context). Also the timer has been deleted, but // before doing so the timer handle has been set to INVALID_HANDLE_VALUE so there is no risk // some other thread is trying to set a deleted timer (besides, we're still holding the // context's critical section hence there can be no such other thread competing here). // release the context here LstRccsUnlockUnref(pIntfContext); // since the resulting IntfContext is passed back to the caller, // make the local pointer NULL (it will be returned later in the out param) pIntfContext = NULL; } // for media connect & disconnect.. if (dwNotifCode == WZCNOTIF_MEDIA_CONNECT || dwNotifCode == WZCNOTIF_MEDIA_DISCONNECT) { // NOTE: keep in mind, pIntfContext is valid because we're in the critical section // for the hashes. // // if there is a context under Zero Conf control, dispatch the event to the // state machine if (pIntfContext != NULL) { // first lock the context since the state machine deals only with locked contexts LstRccsReference(pIntfContext); LstRccsLock(pIntfContext); dwErr = StateDispatchEvent( dwNotifCode == WZCNOTIF_MEDIA_CONNECT ? eEventConnect : eEventDisconnect, pIntfContext, NULL); LstRccsUnlockUnref(pIntfContext); } else { dwErr = ERROR_FILE_NOT_FOUND; } } exit: *ppIntfContext = pIntfContext; DbgPrint((TRC_TRACK,"LstNotificationHandler]=%d", dwErr)); return dwErr; } //----------------------------------------------------------- // Application Command call. // [in] dwHandle: key for identifying the context (state) to which this cmd is referring // [in] dwCmdCode: Command code (one of the WZCCMD_* contants) // [in] wszIntfGuid: the guid of the interface to which this cmd is addressed // [in] prdUserData: Application data associated to this command DWORD LstCmdInterface( DWORD dwHandle, DWORD dwCmdCode, LPWSTR wszIntfGuid, PRAW_DATA prdUserData) { DWORD dwErr = ERROR_FILE_NOT_FOUND; PHASH_NODE pNode = NULL; PINTF_CONTEXT pIntfContext; DbgPrint((TRC_TRACK, "[LstCmdInterface(hdl=0x%x, cmd=0x%x,...)", dwHandle, dwCmdCode)); if (g_lstIntfHashes.bValid) { EnterCriticalSection(&g_lstIntfHashes.csMutex); dwErr = HshQueryObjectRef( g_lstIntfHashes.pHnGUID, wszIntfGuid, &pNode); if (dwErr == ERROR_SUCCESS) { pIntfContext = pNode->pObject; // bump up the reference counter since we're going // to work with this object LstRccsReference(pIntfContext); } LeaveCriticalSection(&g_lstIntfHashes.csMutex); } else dwErr = ERROR_ARENA_TRASHED; // a failure at this point, means there was no context // to lock so we can safely go to 'exit' if (dwErr != ERROR_SUCCESS) goto exit; // Lock the context now LstRccsLock(pIntfContext); // don't do any processing if the handle passed down with the // command doesn't match the session handle (meaning the command // refers to the right iteration loop). if (dwCmdCode == WZCCMD_HARD_RESET || dwHandle == pIntfContext->dwSessionHandle) { ESTATE_EVENT StateEvent; BOOL bIgnore = FALSE; // tells whether the state machine needs to be kicked // for this command. BOOL bCopy = TRUE; // tells whether the user data needs to be copied // in the successful config context. DWORD dwRefreshOIDs = 0; LPVOID pEventData = NULL; PWZC_WLAN_CONFIG pSConfig = NULL; // translate the command code to the internal event switch (dwCmdCode) { case WZCCMD_HARD_RESET: bCopy = FALSE; // no need to copy anything on hard reset! StateEvent = eEventCmdReset; break; case WZCCMD_SOFT_RESET: StateEvent = eEventCmdRefresh; dwRefreshOIDs = INTF_LIST_SCAN; pEventData = &dwRefreshOIDs; break; case WZCCMD_CFG_NEXT: StateEvent = eEventCmdCfgNext; break; case WZCCMD_CFG_DELETE: StateEvent = eEventCmdCfgDelete; break; case WZCCMD_CFG_NOOP: StateEvent = eEventCmdCfgNoop; break; case WZCCMD_CFG_SETDATA: bIgnore = TRUE; break; case WZCCMD_SKEY_QUERY: bIgnore = TRUE; bCopy = FALSE; dwErr = ERROR_SUCCESS; if (prdUserData == NULL) { dwErr = ERROR_INVALID_PARAMETER; } else { if (pIntfContext->pSecSessionKeys == NULL) { prdUserData->dwDataLen = 0; } else if (prdUserData->dwDataLen < sizeof(SESSION_KEYS)) { prdUserData->dwDataLen = sizeof(SESSION_KEYS); dwErr = ERROR_MORE_DATA; } else { PSESSION_KEYS pSK = (PSESSION_KEYS) prdUserData->pData; dwErr = WzcSSKDecrypt(pIntfContext->pSecSessionKeys, pSK); } } break; case WZCCMD_SKEY_SET: bIgnore = TRUE; bCopy = FALSE; dwErr = ERROR_SUCCESS; if (prdUserData == NULL) { WzcSSKFree(pIntfContext->pSecSessionKeys); pIntfContext->pSecSessionKeys = NULL; } else if (prdUserData->dwDataLen != sizeof(SESSION_KEYS)) { dwErr = ERROR_INVALID_PARAMETER; } else { if (pIntfContext->pSecSessionKeys == NULL) { pIntfContext->pSecSessionKeys = MemCAlloc(sizeof(SEC_SESSION_KEYS)); if (pIntfContext->pSecSessionKeys == NULL) dwErr = GetLastError(); } if (dwErr == ERROR_SUCCESS) { PSESSION_KEYS pSK = (PSESSION_KEYS) prdUserData->pData; WzcSSKClean(pIntfContext->pSecSessionKeys); dwErr = WzcSSKEncrypt(pIntfContext->pSecSessionKeys, pSK); } } break; default: // just in case we were just asked to set the BLOB (and we did this already) // or some bogus code came in, no event will be dispatched to the state machine bIgnore = TRUE; break; } // copy down the user data to the config currently selected if (bCopy && pIntfContext->pwzcSList != NULL && pIntfContext->pwzcSList->Index < pIntfContext->pwzcSList->NumberOfItems) { pSConfig = &(pIntfContext->pwzcSList->Config[pIntfContext->pwzcSList->Index]); // if whatever buffer we already have is not large enough, clean it out if (prdUserData == NULL || pSConfig->rdUserData.dwDataLen < prdUserData->dwDataLen) { MemFree(pSConfig->rdUserData.pData); pSConfig->rdUserData.pData = NULL; pSConfig->rdUserData.dwDataLen = 0; } // if a new buffer will be needed, allocate it here. if (prdUserData != NULL && prdUserData->dwDataLen > pSConfig->rdUserData.dwDataLen) { pSConfig->rdUserData.pData = MemCAlloc(prdUserData->dwDataLen); if (pSConfig->rdUserData.pData == NULL) { dwErr = GetLastError(); goto exit; } pSConfig->rdUserData.dwDataLen = prdUserData->dwDataLen; } // if there is any user data to store, do it here if (prdUserData != NULL && prdUserData->dwDataLen > 0) memcpy(pSConfig->rdUserData.pData, prdUserData->pData, prdUserData->dwDataLen); } // if this command is not to be ignored, dispatch the // corresponding state event to the state machine dispatcher. if (!bIgnore) { dwErr = StateDispatchEvent( StateEvent, pIntfContext, pEventData); // clear up the INTFCTL_INTERNAL_BLK_MEDIACONN bit since this is not a media sense handler pIntfContext->dwCtlFlags &= ~INTFCTL_INTERNAL_BLK_MEDIACONN; } } // Unlock the context now LstRccsUnlockUnref(pIntfContext); exit: DbgPrint((TRC_TRACK, "LstCmdInterface]=%d", dwErr)); return dwErr; } //----------------------------------------------------------- // Network Connection's status query // [in] wszIntfGuid: the guid of the interface to which this cmd is addressed // [out] pncs: network connection status, if controlled by WZC. HRESULT LstQueryGUIDNCStatus( LPWSTR wszIntfGuid, NETCON_STATUS *pncs) { DWORD dwErr = ERROR_FILE_NOT_FOUND; HRESULT hr = S_FALSE; PHASH_NODE pNode = NULL; PINTF_CONTEXT pIntfContext; DbgPrint((TRC_TRACK, "[LstQueryGUIDNCStatus(%S)", wszIntfGuid)); if (g_lstIntfHashes.bValid) { EnterCriticalSection(&g_lstIntfHashes.csMutex); dwErr = HshQueryObjectRef( g_lstIntfHashes.pHnGUID, wszIntfGuid, &pNode); if (dwErr == ERROR_SUCCESS) { pIntfContext = pNode->pObject; // bump up the reference counter since we're going // to work with this object LstRccsReference(pIntfContext); } LeaveCriticalSection(&g_lstIntfHashes.csMutex); } else dwErr = ERROR_ARENA_TRASHED; // a failure at this point, means there was no context // to lock so we can safely go to 'exit' if (dwErr != ERROR_SUCCESS) goto exit; // Lock the context now LstRccsLock(pIntfContext); // we control the state only if WZC is enabled and the adapter is // anything else but connected. Otherwise the upper layer protocols // are in control. // // For now (WinXP client RTM), Zero Config should report to NETMAN only the // disconnected state. This is to fix bug #401130 which is NETSHELL displaying // the bogus SSID from the {SF} state, while the IP address is lost and until // the media disconnect is received (10 seconds later). if (pIntfContext->dwCtlFlags & INTFCTL_ENABLED && pIntfContext->dwCtlFlags & INTFCTL_OIDSSUPP && pIntfContext->ncStatus != NCS_CONNECTED) { *pncs = NCS_MEDIA_DISCONNECTED; hr = S_OK; } // Unlock the context now LstRccsUnlockUnref(pIntfContext); exit: DbgPrint((TRC_TRACK, "LstQueryGUIDNCStatus]=%d", dwErr)); return hr; } //----------------------------------------------------------- // Generate the initial dynamic session keys. // [in] pIntfContext: Interface context containing the material for initial key generation. DWORD LstGenInitialSessionKeys( PINTF_CONTEXT pIntfContext) { DWORD dwErr = ERROR_SUCCESS; PWZC_WLAN_CONFIG pSConfig = NULL; NDIS_802_11_MAC_ADDRESS ndMAC[2] = {0}; SESSION_KEYS SessionKeys; UCHAR KeyMaterial[WZCCTL_MAX_WEPK_MATERIAL]; if (pIntfContext->pwzcSList != NULL && pIntfContext->pwzcSList->Index < pIntfContext->pwzcSList->NumberOfItems) { pSConfig = &(pIntfContext->pwzcSList->Config[pIntfContext->pwzcSList->Index]); } if (pSConfig != NULL && pSConfig->dwCtlFlags & WZCCTL_WEPK_PRESENT) { // get the random info needed for the key generation (RemoteMAC | LocalMAC ). pSConfig = &(pIntfContext->pwzcSList->Config[pIntfContext->pwzcSList->Index]); memcpy(&ndMAC[0], &pSConfig->MacAddress, sizeof(NDIS_802_11_MAC_ADDRESS)); memcpy(&ndMAC[1], &pIntfContext->ndLocalMac, sizeof(NDIS_802_11_MAC_ADDRESS)); // generate dynamic keys starting from unscrambled WEP { UINT i; for (i = 0; i < WZCCTL_MAX_WEPK_MATERIAL; i++) KeyMaterial[i] = pSConfig->KeyMaterial[i] ^ g_chFakeKeyMaterial[(7*i)%13]; } dwErr = GenerateDynamicKeys( KeyMaterial, pSConfig->KeyLength, (LPBYTE)&ndMAC[0], sizeof(ndMAC), pSConfig->KeyLength, &SessionKeys); if (dwErr == ERROR_SUCCESS) { WzcSSKFree(pIntfContext->pSecSessionKeys); pIntfContext->pSecSessionKeys = MemCAlloc(sizeof(SEC_SESSION_KEYS)); if (pIntfContext->pSecSessionKeys == NULL) { dwErr = GetLastError(); } else { dwErr = WzcSSKEncrypt(pIntfContext->pSecSessionKeys, &SessionKeys); } } } return dwErr; } //----------------------------------------------------------- // Updates the list of blocked configurations with the selected configurations // that were blocked at this round by the upper layer (marked with WZCCTL_INTERNAL_BLOCKED // in the list of selected configurations) // [in] pIntfContext: Interface context containing the configurations lists DWORD LstUpdateBlockedList( PINTF_CONTEXT pIntfContext) { DWORD dwErr = ERROR_SUCCESS; UINT i, nBlocked = 0; PWZC_802_11_CONFIG_LIST pNewBList = NULL; PWZC_WLAN_CONFIG pConfig; BOOL bChanged = FALSE; DbgPrint((TRC_TRACK, "[LstUpdateBlockedList(0x%p)", pIntfContext)); // the first thing is to count how many blocked configurations we have // check first the current blocked list for blocked configs still "alive" if (pIntfContext->pwzcBList != NULL) { for (i=0; i < pIntfContext->pwzcBList->NumberOfItems; i++) { if (pIntfContext->pwzcBList->Config[i].Reserved[0] > 0) nBlocked++; else bChanged = TRUE; // this entry is going to be removed! } } // check now how many configs are going to be blocked from the current selection list // NOTE: the entries from the SList are guaranteed not to duplicate entries in the BList // If an entry is in the BList it means it was excluded from being added to the SList // when the SList was created. if (pIntfContext->pwzcSList != NULL) { for (i=0; i < pIntfContext->pwzcSList->NumberOfItems; i++) { if (pIntfContext->pwzcSList->Config[i].dwCtlFlags & WZCCTL_INTERNAL_BLOCKED) { nBlocked++; bChanged = TRUE; // a new entry becomes blocked } } } // if we found there are no blocked entries, nor in the original list not in the current // (failed) selection list, just go out successfully - it means the original pwzcBList // is already NULL and it should remain this way if (nBlocked == 0) goto exit; pNewBList = (PWZC_802_11_CONFIG_LIST) MemCAlloc(FIELD_OFFSET(WZC_802_11_CONFIG_LIST, Config) + nBlocked * sizeof(WZC_WLAN_CONFIG)); // on memory allocation error, get out with the error code if (pNewBList == NULL) { dwErr = GetLastError(); goto exit; } // if originally there were some alive blocked entries, copy them over to the new list if (pIntfContext->pwzcBList != NULL) { for (i=0; i < pIntfContext->pwzcBList->NumberOfItems && nBlocked > 0; i++) { pConfig = &(pIntfContext->pwzcBList->Config[i]); if (pConfig->Reserved[0] > 0) { memcpy(&(pNewBList->Config[pNewBList->NumberOfItems]), pConfig, sizeof(WZC_WLAN_CONFIG)); // make sure the copy doesn't include any "user" data: pConfig->rdUserData.pData = NULL; pConfig->rdUserData.dwDataLen = 0; // don't touch anything from this blocked configuration. TTL goes down // by itself with each scan (if network is not available) pNewBList->NumberOfItems++; // make sure we are breaking the loop if we have no storage for any potential // blocked configuration. This shouldn't happen since we were counting these first // and the whole context is locked, but ... it doesn't hurt nBlocked--; } } } // now copy over the new blocked entries, if any if (pIntfContext->pwzcSList != NULL) { for (i=0; i < pIntfContext->pwzcSList->NumberOfItems && nBlocked > 0; i++) { pConfig = &(pIntfContext->pwzcSList->Config[i]); if (pConfig->dwCtlFlags & WZCCTL_INTERNAL_BLOCKED) { memcpy(&(pNewBList->Config[pNewBList->NumberOfItems]), pConfig, sizeof(WZC_WLAN_CONFIG)); // make sure the copy doesn't include any "user" data: pConfig->rdUserData.pData = NULL; pConfig->rdUserData.dwDataLen = 0; // make sure to set the initial TTL for the new blocked configuration pNewBList->Config[pNewBList->NumberOfItems].Reserved[0] = WZC_INTERNAL_BLOCKED_TTL; pNewBList->NumberOfItems++; // make sure we are breaking the loop if we have no storage for any potential // blocked configuration. This shouldn't happen since we were counting these first // and the whole context is locked, but ... it doesn't hurt nBlocked--; } } } // everything is ok - nothing can fail further, so make pNewBList the official pBList WzcCleanupWzcList(pIntfContext->pwzcBList); pIntfContext->pwzcBList = pNewBList; if (bChanged) { DbLogWzcInfo(WZCSVC_BLIST_CHANGED, pIntfContext, pIntfContext->pwzcBList != NULL ? pIntfContext->pwzcBList->NumberOfItems : 0); } exit: DbgPrint((TRC_TRACK, "LstUpdateBlockedList]=%d", dwErr)); return dwErr; } //----------------------------------------------------------- // Checks each of the entries in the locked list against the visible list. If the // entry is visible, its TTL is reset. If it is not, its TTL is decremented. If the // TTL becomes 0, the entry is taken out of the list. // [in] pIntfContext: Interface context containing the configurations lists DWORD LstDeprecateBlockedList( PINTF_CONTEXT pIntfContext) { DWORD dwErr = ERROR_SUCCESS; UINT i; PWZC_WLAN_CONFIG pConfig, pVConfig; BOOL bChanged = FALSE; DbgPrint((TRC_TRACK, "[LstDeprecateBlockedList(0x%p)", pIntfContext)); // nothing to do if there is no list of blocked entries if (pIntfContext->pwzcBList == NULL) goto exit; for (i=0; i < pIntfContext->pwzcBList->NumberOfItems; i++) { pConfig = &(pIntfContext->pwzcBList->Config[i]); // if the blocked entry appears to be visible, reset its TTL if (WzcFindConfig(pIntfContext->pwzcVList, pConfig, 0) != NULL) pConfig->Reserved[0] = WZC_INTERNAL_BLOCKED_TTL; else // else decrement its TTL pConfig->Reserved[0]--; // if the TTL got to 0, the entry needs to be removed from the list // (exchange with the very last one and the list is made 1 entry shorted) if (pConfig->Reserved[0] == 0) { UINT nLastIdx = pIntfContext->pwzcBList->NumberOfItems - 1; // if this is not the very last entry, exchange it with the last one // but first clean it out since it will be unreachable for WzcCleanupWzcList() MemFree(pConfig->rdUserData.pData); if (i != nLastIdx) { memcpy(pConfig, &(pIntfContext->pwzcBList->Config[nLastIdx]), sizeof(WZC_WLAN_CONFIG)); } // make the list one entry shorter since the removed entry is now at the end pIntfContext->pwzcBList->NumberOfItems--; // next time stay on the same index as at this iteration. i--; // now since this went away, note the change bChanged = TRUE; } } if (bChanged) { DbLogWzcInfo(WZCSVC_BLIST_CHANGED, pIntfContext, pIntfContext->pwzcBList->NumberOfItems); } exit: DbgPrint((TRC_TRACK, "LstDeprecateBlockedList]=%d", dwErr)); return dwErr; }