Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1284 lines
54 KiB

  1. #include <precomp.h>
  2. #include "wzcsvc.h"
  3. #include "intflist.h"
  4. #include "utils.h"
  5. #include "deviceio.h"
  6. #include "storage.h"
  7. #include "state.h"
  8. #include "notify.h"
  9. #include "dialog.h"
  10. #include "tracing.h"
  11. //-----------------------------------------------------------
  12. // StateTmSetOneTimeTimer: Sets a one time timer for the given context with the
  13. // hardcoded callback WZCTimeoutCallback() and with the parameter the interface
  14. // context itself.
  15. // Parameters:
  16. // [in/out] pIntfContext: identifies the context for which is set the timer.
  17. // [in] dwMSeconds: miliseconds interval when the timer is due to fire
  18. DWORD
  19. StateTmSetOneTimeTimer(
  20. PINTF_CONTEXT pIntfContext,
  21. DWORD dwMSeconds)
  22. {
  23. DWORD dwErr = ERROR_SUCCESS;
  24. if (pIntfContext->hTimer == INVALID_HANDLE_VALUE)
  25. {
  26. dwErr = ERROR_INVALID_PARAMETER;
  27. }
  28. else if (ChangeTimerQueueTimer(
  29. g_htmQueue,
  30. pIntfContext->hTimer,
  31. dwMSeconds,
  32. TMMS_INFINITE))
  33. {
  34. if (dwMSeconds == TMMS_INFINITE)
  35. pIntfContext->dwCtlFlags &= ~INTFCTL_INTERNAL_TM_ON;
  36. else
  37. pIntfContext->dwCtlFlags |= INTFCTL_INTERNAL_TM_ON;
  38. }
  39. else
  40. {
  41. DbgPrint((TRC_SYNC, "Failed setting the context 0x%p timer to %dms", pIntfContext, dwMSeconds));
  42. dwErr = GetLastError();
  43. }
  44. return dwErr;
  45. }
  46. //-----------------------------------------------------------
  47. // StateDispatchEvent: processes an event that will cause the state machine to transition
  48. // through one or more states.
  49. // Parameters:
  50. // [in] StateEvent: identifies the event that triggers the transition(s)
  51. // [in] pIntfContext: points to the interface that is subject for the transition(s)
  52. // [in] pvEventData: any data related to the event
  53. // NOTE: The caller of this function should already take care of locking the pIntfContext
  54. // in its critical section. The assumption is the Interface Context is already locked.
  55. DWORD
  56. StateDispatchEvent(
  57. ESTATE_EVENT StateEvent,
  58. PINTF_CONTEXT pIntfContext,
  59. PVOID pvEventData)
  60. {
  61. DWORD dwErr = ERROR_SUCCESS;
  62. DbgPrint((TRC_TRACK|TRC_STATE,"[StateDispatchEvent(%d,0x%p,0x%p)", StateEvent, pIntfContext, pvEventData));
  63. DbgAssert((pIntfContext != NULL, "Can't dispatch event for NULL context!"));
  64. // determine the state to transition to, based on the event that is to be dispatched
  65. // whatever the current state is, if the event is eEventAdd, move the context to SI
  66. switch(StateEvent)
  67. {
  68. case eEventAdd:
  69. //Record Event into logging DB
  70. DbLogWzcInfo(WZCSVC_EVENT_ADD, pIntfContext,
  71. pIntfContext->wszDescr);
  72. // on interface addition, no matter what, go straight to {SI}
  73. pIntfContext->pfnStateHandler = StateInitFn;
  74. dwErr = ERROR_CONTINUE;
  75. break;
  76. case eEventTimeout:
  77. // if timeout in {SSr}, transition to {SQ}
  78. if (pIntfContext->pfnStateHandler == StateSoftResetFn)
  79. {
  80. pIntfContext->pfnStateHandler = StateQueryFn;
  81. dwErr = ERROR_CONTINUE;
  82. }
  83. // if timeout in {SDSr}, transition back to {SSr}
  84. else if (pIntfContext->pfnStateHandler == StateDelaySoftResetFn)
  85. {
  86. pIntfContext->pfnStateHandler = StateSoftResetFn;
  87. dwErr = ERROR_CONTINUE;
  88. }
  89. // if timeout in {SF}, transition to {SHr}
  90. else if (pIntfContext->pfnStateHandler == StateFailedFn)
  91. {
  92. pIntfContext->pfnStateHandler = StateHardResetFn;
  93. dwErr = ERROR_CONTINUE;
  94. }
  95. // if timeout in {SIter}, transition to {SRs}
  96. else if (pIntfContext->pfnStateHandler == StateIterateFn)
  97. {
  98. pIntfContext->pfnStateHandler = StateCfgRemoveFn;
  99. dwErr = ERROR_CONTINUE;
  100. }
  101. // if timeout in {SC} or {SCk}, transition to {SSr}
  102. else if (pIntfContext->pfnStateHandler == StateConfiguredFn ||
  103. pIntfContext->pfnStateHandler == StateCfgHardKeyFn)
  104. {
  105. pIntfContext->pfnStateHandler = StateSoftResetFn;
  106. dwErr = ERROR_CONTINUE;
  107. }
  108. // Record Event into logging DB
  109. DbLogWzcInfo(WZCSVC_EVENT_TIMEOUT, pIntfContext);
  110. break;
  111. case eEventConnect:
  112. // for each media connect notification, read the BSSID
  113. // Don't care if it fails (it might in certain cases)
  114. dwErr = DevioRefreshIntfOIDs(pIntfContext, INTF_BSSID, NULL);
  115. // Record Event into logging DB - this should be the first log showing up
  116. DbLogWzcInfo(WZCSVC_EVENT_CONNECT, pIntfContext);
  117. // if there was any error getting the BSSID, log the error here
  118. if (dwErr != ERROR_SUCCESS)
  119. DbLogWzcError(WZCSVC_ERR_QUERRY_BSSID, pIntfContext, dwErr);
  120. // if there is a chance the association is alredy successful
  121. // reset the session keys - if applicable
  122. if (dwErr == ERROR_SUCCESS &&
  123. (pIntfContext->pfnStateHandler == StateConfiguredFn ||
  124. pIntfContext->pfnStateHandler == StateCfgHardKeyFn ||
  125. pIntfContext->pfnStateHandler == StateSoftResetFn ||
  126. pIntfContext->pfnStateHandler == StateQueryFn
  127. )
  128. )
  129. {
  130. dwErr = LstGenInitialSessionKeys(pIntfContext);
  131. // if there was any error setting the initial session keys, log it here
  132. if (dwErr != ERROR_SUCCESS)
  133. DbLogWzcError(WZCSVC_ERR_GEN_SESSION_KEYS, pIntfContext, dwErr);
  134. }
  135. // reset the error id since nothing that happens so far
  136. // is critical enough to stop the state machine.
  137. dwErr = ERROR_SUCCESS;
  138. // if connect in {SIter}, transition to {SN}
  139. if (pIntfContext->pfnStateHandler == StateIterateFn)
  140. {
  141. pIntfContext->pfnStateHandler = StateNotifyFn;
  142. dwErr = ERROR_CONTINUE;
  143. }
  144. break;
  145. case eEventDisconnect:
  146. //Record Event into logging DB
  147. DbLogWzcInfo(WZCSVC_EVENT_DISCONNECT, pIntfContext);
  148. if (pIntfContext->pfnStateHandler == StateSoftResetFn ||
  149. pIntfContext->pfnStateHandler == StateConfiguredFn ||
  150. pIntfContext->pfnStateHandler == StateCfgHardKeyFn)
  151. {
  152. pIntfContext->pfnStateHandler = StateHardResetFn;
  153. dwErr = ERROR_CONTINUE;
  154. }
  155. break;
  156. case eEventCmdRefresh:
  157. //Record Event into logging DB
  158. DbLogWzcInfo(WZCSVC_EVENT_CMDREFRESH, pIntfContext);
  159. if (pvEventData == NULL)
  160. {
  161. dwErr = ERROR_INVALID_PARAMETER;
  162. }
  163. else
  164. {
  165. DWORD dwFlags = *(LPDWORD)pvEventData;
  166. // no matter the state this context is in, if it is not configured
  167. // successfully or during a scan cycle, it will be transitioned to {SHr}
  168. // (need to clear the bvsList, otherwise it could mistakenly land in {SC}, on the
  169. // path {SSr}->{SQ}->{SC}.
  170. if (pIntfContext->pfnStateHandler != StateConfiguredFn &&
  171. pIntfContext->pfnStateHandler != StateCfgHardKeyFn &&
  172. pIntfContext->pfnStateHandler != StateSoftResetFn &&
  173. pIntfContext->pfnStateHandler != StateDelaySoftResetFn)
  174. {
  175. pIntfContext->pfnStateHandler = StateHardResetFn;
  176. dwErr = ERROR_CONTINUE;
  177. }
  178. // if the context is already configured, then we need to either go directly to
  179. // {SSr} if a scan is requested or just to {SQ} if no scan is needed. In the latter
  180. // case, the OIDs will be loaded, and because of the visible list which most probably
  181. // won't be changed (no new scanned happening in between) the context will be
  182. // transitioned instantly back to {SC}
  183. else if (pIntfContext->pfnStateHandler == StateConfiguredFn ||
  184. pIntfContext->pfnStateHandler == StateCfgHardKeyFn)
  185. {
  186. // the refresh command caught the context in {SC} state
  187. // if a scan is requested, transition to {SSr} or
  188. pIntfContext->pfnStateHandler = (dwFlags & INTF_LIST_SCAN) ? StateSoftResetFn : StateQueryFn;
  189. dwErr = ERROR_CONTINUE;
  190. }
  191. // if the context is already in {SSr} or {SDSr} then a scan & full query will
  192. // happen in a matter of seconds. So just return SUCCESS to the call with no other
  193. // action to take.
  194. }
  195. break;
  196. case eEventCmdReset:
  197. //Record Event into logging DB
  198. DbLogWzcInfo(WZCSVC_EVENT_CMDRESET, pIntfContext);
  199. // When this happens, also clean up the blocked list. Any user configuration change should give
  200. // another chance to configurations previously blocked.
  201. WzcCleanupWzcList(pIntfContext->pwzcBList);
  202. pIntfContext->pwzcBList = NULL;
  203. // if reset is requested, no matter what, transition to {SHr}
  204. pIntfContext->pfnStateHandler = StateHardResetFn;
  205. dwErr = ERROR_CONTINUE;
  206. break;
  207. case eEventCmdCfgDelete:
  208. case eEventCmdCfgNext:
  209. //Record Event into logging DB
  210. DbLogWzcInfo((StateEvent == eEventCmdCfgDelete? WZCSVC_EVENT_CMDCFGDELETE : WZCSVC_EVENT_CMDCFGNEXT), pIntfContext);
  211. if (pIntfContext->pfnStateHandler == StateConfiguredFn ||
  212. pIntfContext->pfnStateHandler == StateCfgHardKeyFn ||
  213. pIntfContext->pfnStateHandler == StateSoftResetFn)
  214. {
  215. if (StateEvent == eEventCmdCfgDelete)
  216. {
  217. // mark in the control bits that this removal is forced
  218. pIntfContext->dwCtlFlags |= INTFCTL_INTERNAL_FORCE_CFGREM;
  219. pIntfContext->pfnStateHandler = StateCfgRemoveFn;
  220. }
  221. else
  222. pIntfContext->pfnStateHandler = StateCfgPreserveFn;
  223. dwErr = ERROR_CONTINUE;
  224. }
  225. break;
  226. case eEventCmdCfgNoop:
  227. //Record Event into logging DB
  228. DbLogWzcInfo(WZCSVC_EVENT_CMDCFGNOOP, pIntfContext);
  229. if (pIntfContext->pfnStateHandler == StateCfgHardKeyFn)
  230. {
  231. // mark in the control bits that this removal is forced
  232. pIntfContext->dwCtlFlags |= INTFCTL_INTERNAL_FORCE_CFGREM;
  233. pIntfContext->pfnStateHandler = StateCfgRemoveFn;
  234. dwErr = ERROR_CONTINUE;
  235. }
  236. break;
  237. }
  238. // if this event is not going to be ignored, dwErr is ERROR_CONTINUE at this point.
  239. // otherwise is ERROR_SUCCESS.
  240. // So, if this event is NOT going to be ignored, reset whatever timer
  241. // might have had for the related context. Keep in mind this call is already locking
  242. // the context so if the timer fired already, there is no sync problem
  243. if (dwErr == ERROR_CONTINUE)
  244. {
  245. TIMER_RESET(pIntfContext, dwErr);
  246. // restore the "continue" in case of success
  247. if (dwErr == ERROR_SUCCESS)
  248. dwErr = ERROR_CONTINUE;
  249. }
  250. // if the event is to be processed, dwErr is ERROR_CONTINUE.
  251. // In order to handle the automatic transitions, each State Handler function should set
  252. // the pfnStateHandler field to the state handler where the automatic transition goes and
  253. // also it should return ERROR_CONTINUE. Any other error code means the current
  254. // processing stops. Future transitions will be triggered by future event/timer timeout.
  255. while (dwErr == ERROR_CONTINUE)
  256. {
  257. dwErr = (*(pIntfContext->pfnStateHandler))(pIntfContext);
  258. }
  259. DbgPrint((TRC_TRACK|TRC_STATE,"StateDispatchEvent]=%d", dwErr));
  260. return dwErr;
  261. }
  262. //-----------------------------------------------------------
  263. // StateInitFn: Handler for the Init State.
  264. // This function runs in the context's critical section
  265. DWORD
  266. StateInitFn(PINTF_CONTEXT pIntfContext)
  267. {
  268. DWORD dwErr = ERROR_SUCCESS;
  269. DbgPrint((TRC_TRACK|TRC_STATE,"[StateInitFn(0x%p)", pIntfContext));
  270. DbgAssert((pIntfContext != NULL,"Invalid NULL context in {SI} state"));
  271. //Record current state into logging DB
  272. DbLogWzcInfo(WZCSVC_SM_STATE_INIT, pIntfContext, pIntfContext->wszDescr);
  273. // for this new interface, load its settings from the registry
  274. dwErr = StoLoadIntfConfig(NULL, pIntfContext);
  275. DbgAssert((dwErr == ERROR_SUCCESS,
  276. "StoLoadIntfConfig failed for Intf context 0x%p",
  277. pIntfContext));
  278. if (dwErr == ERROR_SUCCESS && g_wzcInternalCtxt.bValid)
  279. {
  280. PINTF_CONTEXT pIntfTContext;
  281. // apply the global template to this newly created interface
  282. EnterCriticalSection(&g_wzcInternalCtxt.csContext);
  283. pIntfTContext = g_wzcInternalCtxt.pIntfTemplate;
  284. LstRccsReference(pIntfTContext);
  285. LeaveCriticalSection(&g_wzcInternalCtxt.csContext);
  286. LstRccsLock(pIntfTContext);
  287. dwErr = LstApplyTemplate(
  288. pIntfTContext,
  289. pIntfContext,
  290. NULL);
  291. LstRccsUnlockUnref(pIntfTContext);
  292. }
  293. if (dwErr == ERROR_SUCCESS)
  294. {
  295. // getting the interface status (media type & media state)
  296. dwErr = DevioGetIntfStats(pIntfContext);
  297. DbgAssert((dwErr == ERROR_SUCCESS,
  298. "DevioGetIntfStats failed for Intf context 0x%p",
  299. pIntfContext));
  300. // getting the interface MAC address
  301. dwErr = DevioGetIntfMac(pIntfContext);
  302. DbgAssert((dwErr == ERROR_SUCCESS,
  303. "DevioGetIntfMac failed for Intf context 0x%p",
  304. pIntfContext));
  305. DbgBinPrint((TRC_TRACK, "Local Mac address :",
  306. pIntfContext->ndLocalMac, sizeof(NDIS_802_11_MAC_ADDRESS)));
  307. }
  308. // fail initialization if the interface is not a wireless adapter
  309. if (dwErr == ERROR_SUCCESS &&
  310. pIntfContext->ulPhysicalMediaType != NdisPhysicalMediumWirelessLan)
  311. dwErr = ERROR_MEDIA_INCOMPATIBLE;
  312. // do a preliminary check on the OIDs
  313. if (dwErr == ERROR_SUCCESS)
  314. {
  315. DWORD dwLErr;
  316. dwLErr = DevioRefreshIntfOIDs(
  317. pIntfContext,
  318. INTF_INFRAMODE|INTF_AUTHMODE|INTF_WEPSTATUS|INTF_SSID|INTF_BSSIDLIST,
  319. NULL);
  320. // if the query succeeded, then assume the NIC supports the OIDs needed for Zero Config
  321. if (dwLErr == ERROR_SUCCESS)
  322. {
  323. pIntfContext->dwCtlFlags |= INTFCTL_OIDSSUPP;
  324. }
  325. // otherwise don't make this determination now - it could be a failure caused by the
  326. // device booting up.
  327. }
  328. // if all went well, prepare an automatic transition to {SHr}
  329. if (dwErr == ERROR_SUCCESS)
  330. {
  331. // set the "signal" control bit
  332. pIntfContext->dwCtlFlags |= INTFCTL_INTERNAL_SIGNAL;
  333. pIntfContext->pfnStateHandler = StateHardResetFn;
  334. // at this point, if the service is not enabled on this wireless interface
  335. // Notify the stack (TCP) of the failure in order to unblock the NetReady notification.
  336. if (!(pIntfContext->dwCtlFlags & INTFCTL_ENABLED) &&
  337. !(pIntfContext->dwCtlFlags & INTFCTL_INTERNAL_INITFAILNOTIF))
  338. {
  339. // call into the stack - don't care about the return code.
  340. DevioNotifyFailure(pIntfContext->wszGuid);
  341. // make sure this call is never done twice for this adapter
  342. pIntfContext->dwCtlFlags |= INTFCTL_INTERNAL_INITFAILNOTIF;
  343. }
  344. dwErr = ERROR_CONTINUE;
  345. }
  346. DbgPrint((TRC_TRACK|TRC_STATE,"StateInitFn]=%d", dwErr));
  347. return dwErr;
  348. }
  349. //-----------------------------------------------------------
  350. // StateHardResetFn: Handler for the {SHr} state
  351. DWORD
  352. StateHardResetFn(PINTF_CONTEXT pIntfContext)
  353. {
  354. DWORD dwErr = ERROR_SUCCESS;
  355. DbgPrint((TRC_TRACK|TRC_STATE,"[StateHardResetFn(0x%p)", pIntfContext));
  356. DbgAssert((pIntfContext != NULL,"Invalid NULL context in {SHr} state"));
  357. // in this state we are surely not associated.
  358. ZeroMemory(pIntfContext->wzcCurrent.MacAddress, sizeof(NDIS_802_11_MAC_ADDRESS));
  359. // Record current state into logging DB
  360. DbLogWzcInfo(WZCSVC_SM_STATE_HARDRESET, pIntfContext);
  361. // once in this state, the ncstatus should report "connecting"
  362. pIntfContext->ncStatus = NCS_CONNECTING;
  363. // if the service is enabled, notify netman about the current ncstatus
  364. if (pIntfContext->dwCtlFlags & INTFCTL_ENABLED)
  365. WzcNetmanNotify(pIntfContext);
  366. // Bump up the session handler for this intf context,
  367. // since it starts plumbing new configs. No commands from older
  368. // iterations should be accepted from now on.
  369. pIntfContext->dwSessionHandle++;
  370. // on hard reset, reopen NDISUIO handle and get the current SSID
  371. dwErr = DevioRefreshIntfOIDs(
  372. pIntfContext,
  373. INTF_HANDLE|INTF_SSID,
  374. NULL);
  375. // ignore whatever error is encountered here..
  376. // If there is an error, then the interface handle will be invalid
  377. // and it will be internally reopened in the {SSr} state
  378. // at this point make sure the card won't get randomly associate
  379. // during the subsequent network scan. We make this happen by plumbing
  380. // down a random non-visible SSID but only in the following cases:
  381. // - the service is enabled (otherwise no config change is allowed)
  382. // - the current SSID was retrieved successfully
  383. // - there is an SSID returned by the driver
  384. // - the current SSID shows as being NULL (all filled with 0 chars)
  385. if (pIntfContext->dwCtlFlags & INTFCTL_ENABLED &&
  386. dwErr == ERROR_SUCCESS &&
  387. WzcIsNullBuffer(pIntfContext->wzcCurrent.Ssid.Ssid, pIntfContext->wzcCurrent.Ssid.SsidLength))
  388. {
  389. BYTE chSSID[32];
  390. DbgPrint((TRC_STATE,"Overwriting null SSID before scan"));
  391. ZeroMemory(&chSSID, sizeof(chSSID));
  392. if (WzcRndGenBuffer(chSSID, 32, 1, 31) == ERROR_SUCCESS)
  393. {
  394. INTF_ENTRY IntfEntry;
  395. ZeroMemory(&IntfEntry, sizeof(INTF_ENTRY));
  396. IntfEntry.rdSSID.pData = chSSID;
  397. IntfEntry.rdSSID.dwDataLen = 32;
  398. IntfEntry.nInfraMode = Ndis802_11Infrastructure;
  399. DevioSetIntfOIDs(
  400. pIntfContext,
  401. &IntfEntry,
  402. INTF_SSID | INTF_INFRAMODE,
  403. NULL);
  404. // this is not an SSID we need to remember (being a random one)
  405. ZeroMemory(&(pIntfContext->wzcCurrent.Ssid), sizeof(NDIS_802_11_SSID));
  406. }
  407. }
  408. // on hard reset, free the current selection list. This way,
  409. // whatever new selection list we build later will have all
  410. // new networks and a configuration will be forcefully plumbed
  411. WzcCleanupWzcList(pIntfContext->pwzcSList);
  412. pIntfContext->pwzcSList = NULL;
  413. // automatic transition to {SSr} state
  414. pIntfContext->pfnStateHandler = StateSoftResetFn;
  415. dwErr = ERROR_CONTINUE;
  416. DbgPrint((TRC_TRACK|TRC_STATE,"StateHardResetFn]=%d", dwErr));
  417. return dwErr;
  418. }
  419. //-----------------------------------------------------------
  420. // StateSoftResetFn: Handler for the {SSr} state
  421. DWORD
  422. StateSoftResetFn(PINTF_CONTEXT pIntfContext)
  423. {
  424. DWORD dwErr = ERROR_SUCCESS;
  425. DbgPrint((TRC_TRACK|TRC_STATE,"[StateSoftResetFn(0x%p)", pIntfContext));
  426. DbgAssert((pIntfContext != NULL,"Invalid NULL context in {SSr} state"));
  427. //Record current state into logging DB
  428. DbLogWzcInfo(WZCSVC_SM_STATE_SOFTRESET, pIntfContext);
  429. DbgPrint((TRC_STATE,"Delay {SSr} on failure? %s",
  430. (pIntfContext->dwCtlFlags & INTFCTL_INTERNAL_NO_DELAY) ? "No" : "Yes"));
  431. // indicate to the driver to rescan the BSSID_LIST for this adapter
  432. dwErr = DevioRefreshIntfOIDs(
  433. pIntfContext,
  434. INTF_LIST_SCAN,
  435. NULL);
  436. if (dwErr == ERROR_SUCCESS)
  437. {
  438. // once we passed through this state, allow again delayed
  439. // execution of {SSr} in future loops.
  440. pIntfContext->dwCtlFlags &= ~INTFCTL_INTERNAL_NO_DELAY;
  441. // set the rescan timer
  442. TIMER_SET(pIntfContext, TMMS_Tr, dwErr);
  443. // when the timer will be fired off, the dispatcher will
  444. // take care to transit this context into the {SQ} state.
  445. }
  446. else
  447. {
  448. // it happens that after resume from standby WZC is waken up before the
  449. // adapter being bound correctly to NDISUIO in which case, scanning the networks
  450. // return ERROR_NOT_SUPPORTED. So, just to play safe, in case of any error just give
  451. // it another try in a couple of seconds.
  452. if (!(pIntfContext->dwCtlFlags & INTFCTL_INTERNAL_NO_DELAY))
  453. {
  454. // once we passed through this state, don't allow further delayed execution
  455. pIntfContext->dwCtlFlags |= INTFCTL_INTERNAL_NO_DELAY;
  456. pIntfContext->pfnStateHandler = StateDelaySoftResetFn;
  457. }
  458. else
  459. {
  460. // once we passed through this state, allow again delayed
  461. // execution of {SSr} in future loops
  462. // also, if the OIDs are failing, don't pop up any balloon.
  463. pIntfContext->dwCtlFlags &= ~(INTFCTL_INTERNAL_NO_DELAY|INTFCTL_INTERNAL_SIGNAL);
  464. // regardless the error, just go over it and assume the driver has already the list
  465. // of SSIDs and all the other OIDs we need. Will use that one hence we have to go on to {SQ}.
  466. pIntfContext->pfnStateHandler = StateQueryFn;
  467. }
  468. dwErr = ERROR_CONTINUE;
  469. }
  470. DbgPrint((TRC_TRACK|TRC_STATE,"StateSoftResetFn]=%d", dwErr));
  471. return dwErr;
  472. }
  473. //-----------------------------------------------------------
  474. // StateDelaySoftResetFn: Handler for the {SDSr} state
  475. DWORD
  476. StateDelaySoftResetFn(
  477. PINTF_CONTEXT pIntfContext)
  478. {
  479. DWORD dwErr = ERROR_SUCCESS;
  480. DbgPrint((TRC_TRACK|TRC_STATE,"[StateDelaySoftResetFn(0x%p)", pIntfContext));
  481. DbgAssert((pIntfContext != NULL,"Invalid NULL context in {SDSr} state"));
  482. DbLogWzcInfo(WZCSVC_SM_STATE_DELAY_SR, pIntfContext);
  483. // if there was a failure in {SSr} such that we had to delay and retry that state
  484. // then refresh the interface handle in an attempt to recover from the error
  485. DevioRefreshIntfOIDs(
  486. pIntfContext,
  487. INTF_HANDLE,
  488. NULL);
  489. // set the timer to retry the {SSr} state
  490. TIMER_SET(pIntfContext, TMMS_Td, dwErr);
  491. DbgPrint((TRC_TRACK|TRC_STATE,"StateDelaySoftResetFn]=%d", dwErr));
  492. return dwErr;
  493. }
  494. //-----------------------------------------------------------
  495. // StateQueryFn: Handler for the {SQ} state
  496. DWORD
  497. StateQueryFn(PINTF_CONTEXT pIntfContext)
  498. {
  499. DWORD dwErr = ERROR_SUCCESS;
  500. DbgPrint((TRC_TRACK|TRC_STATE,"[StateQueryFn(0x%p)", pIntfContext));
  501. DbgAssert((pIntfContext != NULL,"Invalid NULL context in {SQ} state"));
  502. DbgAssert((pIntfContext->hIntf != INVALID_HANDLE_VALUE,"Invalid Ndisuio handle in {SQ} state"));
  503. //Record current state into logging DB
  504. DbLogWzcInfo(WZCSVC_SM_STATE_QUERY, pIntfContext);
  505. dwErr = DevioGetIntfStats(pIntfContext);
  506. // don't care much about the result of this call..
  507. DbgAssert((dwErr == ERROR_SUCCESS, "Getting NDIS statistics failed in state {SQ}"));
  508. // check the media state (just for debugging)
  509. DbgPrint((TRC_GENERIC, "Media State (%d) is %s",
  510. pIntfContext->ulMediaState,
  511. pIntfContext->ulMediaState == MEDIA_STATE_CONNECTED ? "Connected" : "Not Connected"));
  512. dwErr = DevioRefreshIntfOIDs(
  513. pIntfContext,
  514. INTF_INFRAMODE|INTF_AUTHMODE|INTF_WEPSTATUS|INTF_SSID|INTF_BSSIDLIST,
  515. NULL);
  516. // dwErr is success only in the case all the queries above succeeded
  517. if (dwErr == ERROR_SUCCESS)
  518. {
  519. PWZC_802_11_CONFIG_LIST pwzcSList = NULL;
  520. pIntfContext->dwCtlFlags |= INTFCTL_OIDSSUPP;
  521. // deprecate entries in the blocked list according to the new visible list.
  522. // Entries in the BList which are not seen as "visible" for WZC_INTERNAL_BLOCKED_TTL
  523. // number of times are taken out of that list.
  524. // this function can't fail, this is why we don't check its return value.
  525. LstDeprecateBlockedList(pIntfContext);
  526. // build the pwzcSList out of pwzcVList and pwzcPList and
  527. // taking into consideration the fallback flag
  528. dwErr = LstBuildSelectList(pIntfContext, &pwzcSList);
  529. if (dwErr == ERROR_SUCCESS)
  530. {
  531. UINT nSelIdx = 0;
  532. // check the new selection list against the previous one and see
  533. // if a new plumbing is required or not.
  534. if (LstChangedSelectList(pIntfContext, pwzcSList, &nSelIdx))
  535. {
  536. // if a new plumbing is required, get rid of the old selection
  537. // list and put the new one in the interface context.
  538. WzcCleanupWzcList(pIntfContext->pwzcSList);
  539. pIntfContext->pwzcSList = pwzcSList;
  540. // Make sure we default clear this flag - it will be set a bit down if
  541. // indeed this turns out to be a "one time" deal.
  542. pIntfContext->dwCtlFlags &= ~INTFCTL_INTERNAL_ONE_TIME;
  543. // if the preferred list also includes a starting index, then this
  544. // is a request for a "one time configuration"
  545. if (pIntfContext->pwzcPList != NULL &&
  546. pIntfContext->pwzcPList->Index < pIntfContext->pwzcPList->NumberOfItems)
  547. {
  548. // reset the "start from" index in the preferred list as this is intended
  549. // for "one time configuration" mostly.
  550. pIntfContext->pwzcPList->Index = pIntfContext->pwzcPList->NumberOfItems;
  551. // but keep in mind this is a one time configuration
  552. pIntfContext->dwCtlFlags |= INTFCTL_INTERNAL_ONE_TIME;
  553. }
  554. // set the starting index in the selection list, if there is one
  555. if (pIntfContext->pwzcSList != NULL)
  556. pIntfContext->pwzcSList->Index = nSelIdx;
  557. // then go to the {SIter}
  558. pIntfContext->pfnStateHandler = StateIterateFn;
  559. }
  560. else
  561. {
  562. PWZC_WLAN_CONFIG pConfig;
  563. pConfig = &(pIntfContext->pwzcSList->Config[pIntfContext->pwzcSList->Index]);
  564. DbLogWzcInfo(WZCSVC_SM_STATE_QUERY_NOCHANGE, pIntfContext,
  565. DbLogFmtSSID(0, &(pConfig->Ssid)));
  566. // if no new plumbing is required clean the new selection list
  567. WzcCleanupWzcList(pwzcSList);
  568. // then go to {SC} or {SCk} (depending the WEP key) since the interface context
  569. // has not changed a bit. The selection list & the selection index were not
  570. // touched in this cycle
  571. if (pIntfContext->dwCtlFlags & INTFCTL_INTERNAL_FAKE_WKEY)
  572. pIntfContext->pfnStateHandler = StateCfgHardKeyFn;
  573. else
  574. pIntfContext->pfnStateHandler = StateConfiguredFn;
  575. }
  576. }
  577. }
  578. else
  579. {
  580. // since the oids failed, suppress any subsequent balloon:
  581. pIntfContext->dwCtlFlags &= ~(INTFCTL_OIDSSUPP|INTFCTL_INTERNAL_SIGNAL);
  582. pIntfContext->pfnStateHandler = StateFailedFn;
  583. }
  584. // in both cases, this is an automatic transition
  585. dwErr = ERROR_CONTINUE;
  586. DbgPrint((TRC_TRACK|TRC_STATE,"StateQueryFn]=%d", dwErr));
  587. return dwErr;
  588. }
  589. //-----------------------------------------------------------
  590. // StateIterateFn: Handler for the {SIter} state
  591. DWORD
  592. StateIterateFn(
  593. PINTF_CONTEXT pIntfContext)
  594. {
  595. DWORD dwErr = ERROR_SUCCESS;
  596. PWZC_WLAN_CONFIG pConfig;
  597. DbgPrint((TRC_TRACK|TRC_STATE,"[StateIterateFn(0x%p)", pIntfContext));
  598. DbgAssert((pIntfContext != NULL,"Invalid NULL context in {SIter} state"));
  599. DbgAssert((pIntfContext->hIntf != INVALID_HANDLE_VALUE,"Invalid Ndisuio handle in {SIter} state"));
  600. // Bump up the session handler for this intf context,
  601. // since it starts plumbing new configs. No commands from older
  602. // iterations should be accepted from now on.
  603. pIntfContext->dwSessionHandle++;
  604. // if either zero conf service is disabled for this context or there are no more configurations
  605. // to try, transit to {SF} directly
  606. if (!(pIntfContext->dwCtlFlags & INTFCTL_ENABLED) ||
  607. pIntfContext->pwzcSList == NULL ||
  608. pIntfContext->pwzcSList->NumberOfItems <= pIntfContext->pwzcSList->Index)
  609. {
  610. dwErr = ERROR_CONTINUE;
  611. }
  612. // check if the current config is marked "deleted". If this is the case, it means the {SRs} state
  613. // exhausted all configurations. We should move to the failure state {SF}. The list of selected networks
  614. // remains untouched - it is used in {SF} to update the blocked list. It is cleaned up in {SHr}.
  615. else
  616. {
  617. pConfig = &(pIntfContext->pwzcSList->Config[pIntfContext->pwzcSList->Index]);
  618. if (pConfig->dwCtlFlags & WZCCTL_INTERNAL_DELETED)
  619. {
  620. dwErr = ERROR_CONTINUE;
  621. }
  622. }
  623. if (dwErr == ERROR_SUCCESS)
  624. {
  625. DbgPrint((TRC_STATE,"Plumbing config %d", pIntfContext->pwzcSList->Index));
  626. //Record current state into logging DB
  627. DbLogWzcInfo(WZCSVC_SM_STATE_ITERATE, pIntfContext,
  628. DbLogFmtSSID(0, &(pConfig->Ssid)), pConfig->InfrastructureMode);
  629. // in this state we need to mark ncstatus = "connecting" again. We do it because we could
  630. // come from a connected state, from {SRs} and {SPs}.
  631. pIntfContext->ncStatus = NCS_CONNECTING;
  632. // notify netman about the ncstatus change.
  633. WzcNetmanNotify(pIntfContext);
  634. // here we're about to plumb down a possibly new network. If it is indeed an SSID different from
  635. // the one we have, we'll release the IP address (to force a Discover in the new net). Note that
  636. // in {SIter}->{SRs}->{SIter} loops, no Release happens since the SSID should always coincide.
  637. // Release might happen only on Hard resets when the SSID looks to be different from what is
  638. // about to be plumbed down.
  639. if (pConfig->Ssid.SsidLength != pIntfContext->wzcCurrent.Ssid.SsidLength ||
  640. memcmp(pConfig->Ssid.Ssid, pIntfContext->wzcCurrent.Ssid.Ssid, pConfig->Ssid.SsidLength))
  641. {
  642. DbgPrint((TRC_STATE,"Requesting the release of the DHCP lease"));
  643. // since Zero Configuration is the only one knowing that we are roaming to a new network
  644. // it is up to Zero Configuration to trigger DHCP client lease refreshing. Otherwise DHCP
  645. // client will act only on the Media Connect hence it will try to renew its old lease and
  646. // being on the wrong network this will take a minute. Too long to wait.
  647. DhcpReleaseParameters(pIntfContext->wszGuid);
  648. }
  649. else
  650. DbgPrint((TRC_STATE,"Plumbing down the current SSID => skip the DHCP lease releasing"));
  651. // we do have some entries in the selected configuration list (pwzcSList), and we
  652. // do expect to have a valid index pointing in the list to the selected config
  653. dwErr = LstSetSelectedConfig(pIntfContext, NULL);
  654. if (dwErr == ERROR_SUCCESS)
  655. {
  656. // if everything went ok, we'll expect a media connect event in TMMS_Tp time.
  657. // set this timer to fire up if the event doesn't come in.
  658. TIMER_SET(pIntfContext, TMMS_Tp, dwErr);
  659. }
  660. else
  661. {
  662. DbgPrint((TRC_STATE,"Remove the selected config since the driver failed setting it"));
  663. // if anything went wrong on setting the selected config, don't bail out. Just remove
  664. // this selected config and move to the next one
  665. pIntfContext->dwCtlFlags |= INTFCTL_INTERNAL_FORCE_CFGREM;
  666. pIntfContext->pfnStateHandler = StateCfgRemoveFn;
  667. dwErr = ERROR_CONTINUE;
  668. }
  669. }
  670. else // dwErr can't be anything else bug ERROR_CONTINUE in the 'else' branch
  671. {
  672. DbgPrint((TRC_STATE,"No configurations left in the selection list"));
  673. //Record current state into logging DB
  674. DbLogWzcInfo(WZCSVC_SM_STATE_ITERATE_NONET, pIntfContext);
  675. pIntfContext->pfnStateHandler = StateFailedFn;
  676. }
  677. DbgPrint((TRC_TRACK|TRC_STATE,"StateIterateFn]=%d", dwErr));
  678. return dwErr;
  679. }
  680. //-----------------------------------------------------------
  681. // StateConfiguredFn: Handler for the {SC} state
  682. DWORD
  683. StateConfiguredFn(
  684. PINTF_CONTEXT pIntfContext)
  685. {
  686. DWORD dwErr = ERROR_SUCCESS;
  687. DbgPrint((TRC_TRACK|TRC_STATE,"[StateConfiguredFn(0x%p)", pIntfContext));
  688. DbgAssert((pIntfContext != NULL,"Invalid NULL context in {SQ} state"));
  689. // since we're in {SC}, it means we cannot have a fake WEP Key, no matter what.
  690. // hence, reset the INTFCTL_INTERNAL_FAKE_WKEY flag
  691. pIntfContext->dwCtlFlags &= ~INTFCTL_INTERNAL_FAKE_WKEY;
  692. // set the timer for the "configured" state life time
  693. TIMER_SET(pIntfContext, TMMS_Tc, dwErr);
  694. DbgPrint((TRC_TRACK|TRC_STATE,"StateConfiguredFn]=%d", dwErr));
  695. return dwErr;
  696. }
  697. //-----------------------------------------------------------
  698. // StateFailedFn: Handler for the {SF} state
  699. DWORD
  700. StateFailedFn(
  701. PINTF_CONTEXT pIntfContext)
  702. {
  703. DWORD dwErr = ERROR_SUCCESS;
  704. DbgPrint((TRC_TRACK|TRC_STATE,"[StateFailedFn(0x%p)", pIntfContext));
  705. DbgAssert((pIntfContext != NULL,"Invalid NULL context in {SF} state"));
  706. // Record current state into logging DB
  707. DbLogWzcInfo(WZCSVC_SM_STATE_FAILED, pIntfContext);
  708. //
  709. // a couple of things need to be done if the service is enabled and
  710. // the driver supports all the needed OIDs
  711. if (pIntfContext->dwCtlFlags & INTFCTL_OIDSSUPP &&
  712. pIntfContext->dwCtlFlags & INTFCTL_ENABLED)
  713. {
  714. BYTE chSSID[32];
  715. // send the failure notification down to TCP. This will cause TCP to
  716. // generate the NetReady notification asap.
  717. if (!(pIntfContext->dwCtlFlags & INTFCTL_INTERNAL_INITFAILNOTIF))
  718. {
  719. // call into the stack
  720. DevioNotifyFailure(pIntfContext->wszGuid);
  721. // make sure this call is never done twice for this adapter
  722. pIntfContext->dwCtlFlags |= INTFCTL_INTERNAL_INITFAILNOTIF;
  723. }
  724. // whatever brought us here, the ncstatus should be "disconnected"
  725. pIntfContext->ncStatus = NCS_MEDIA_DISCONNECTED;
  726. // since the service is enabled, notify netman about the status change
  727. WzcNetmanNotify(pIntfContext);
  728. // this is when we should signal the failure if the signal is
  729. // not to be suppressed and the service is enabled
  730. if (pIntfContext->dwCtlFlags & INTFCTL_INTERNAL_SIGNAL)
  731. {
  732. WZCDLG_DATA dialogData = {0};
  733. // once a signal has been generated, suppress further signals
  734. // until passing through a successful configuration
  735. pIntfContext->dwCtlFlags &= ~INTFCTL_INTERNAL_SIGNAL;
  736. // bring up the "Failure" balloon
  737. dialogData.dwCode = WZCDLG_FAILED;
  738. dialogData.lParam = 0;
  739. // count exactly how many visible configs we have (don't count
  740. // SSIDs coming from APs that don't respond to broadcast SSID)
  741. if (pIntfContext->pwzcVList != NULL)
  742. {
  743. UINT i;
  744. for (i = 0; i < pIntfContext->pwzcVList->NumberOfItems; i++)
  745. {
  746. PNDIS_802_11_SSID pndSSID = &(pIntfContext->pwzcVList->Config[i].Ssid);
  747. if (!WzcIsNullBuffer(pndSSID->Ssid, pndSSID->SsidLength))
  748. dialogData.lParam++;
  749. }
  750. }
  751. DbgPrint((TRC_STATE,"Generating balloon notification for %d visible networks", dialogData.lParam));
  752. WzcDlgNotify(pIntfContext, &dialogData);
  753. }
  754. // just break the association here by plumbing down a hard coded SSID
  755. // don't care about the return value.
  756. ZeroMemory(&chSSID, sizeof(chSSID));
  757. if (WzcRndGenBuffer(chSSID, 32, 1, 31) == ERROR_SUCCESS)
  758. {
  759. INTF_ENTRY IntfEntry;
  760. ZeroMemory(&IntfEntry, sizeof(INTF_ENTRY));
  761. IntfEntry.rdSSID.pData = chSSID;
  762. IntfEntry.rdSSID.dwDataLen = 32;
  763. IntfEntry.nInfraMode = Ndis802_11Infrastructure;
  764. DevioSetIntfOIDs(
  765. pIntfContext,
  766. &IntfEntry,
  767. INTF_SSID | INTF_INFRAMODE,
  768. NULL);
  769. // this is not an SSID we need to remember (being a random one)
  770. ZeroMemory(&(pIntfContext->wzcCurrent.Ssid), sizeof(NDIS_802_11_SSID));
  771. }
  772. // regardless the above, update the "blocked" list
  773. dwErr = LstUpdateBlockedList(pIntfContext);
  774. DbgAssert((dwErr == ERROR_SUCCESS, "Failed with err %d updating the list of blocked configs!", dwErr));
  775. }
  776. // Bump up the session handler for this intf context,
  777. // since it starts plumbing new configs. No commands from older
  778. // iterations should be accepted from now on.
  779. pIntfContext->dwSessionHandle++;
  780. // If the card is courteous enough and talks to us, set a timer
  781. // for 1 minute such that after this time we're getting an updated
  782. // picture of what networks are around - at least that should be presented
  783. // to the user.
  784. if (pIntfContext->dwCtlFlags & INTFCTL_OIDSSUPP)
  785. {
  786. // Remain in {SF} one minute and scan again after that. It might look meaningless to do so
  787. // in case the service is disabled, but keep in mind "disabled" means only "don't change
  788. // anything on the card" (aside from the scan). That is, the view of what networks are
  789. // available needs to be kept updated.
  790. TIMER_SET(pIntfContext, TMMS_Tf, dwErr);
  791. }
  792. DbgPrint((TRC_TRACK|TRC_STATE,"StateFailedFn]=%d", dwErr));
  793. return dwErr;
  794. }
  795. //-----------------------------------------------------------
  796. // StateCfgRemoveFn: Handler for the {SRs} state
  797. DWORD
  798. StateCfgRemoveFn(
  799. PINTF_CONTEXT pIntfContext)
  800. {
  801. DWORD dwErr = ERROR_SUCCESS;
  802. BOOL bConnectOK = FALSE;
  803. PWZC_WLAN_CONFIG pConfig = &(pIntfContext->pwzcSList->Config[pIntfContext->pwzcSList->Index]);
  804. DbgPrint((TRC_TRACK|TRC_STATE,"[StateCfgRemoveFn(0x%p)", pIntfContext));
  805. DbgAssert((pIntfContext != NULL,"Invalid NULL context in {SRs} state"));
  806. DbgAssert((pIntfContext->pwzcSList != NULL, "Invalid null selection list in {SRs} state"));
  807. DbgAssert((pIntfContext->pwzcSList->Index < pIntfContext->pwzcSList->NumberOfItems, "Invalid selection index in {SRs} state"));
  808. dwErr = DevioGetIntfStats(pIntfContext);
  809. // debug print
  810. DbgPrint((TRC_GENERIC, "Media State (%d) is %s",
  811. pIntfContext->ulMediaState,
  812. pIntfContext->ulMediaState == MEDIA_STATE_CONNECTED ? "Connected" : "Not Connected"));
  813. // if we were explicitly requested to delete this configuration, do it so no matter what
  814. if (pIntfContext->dwCtlFlags & INTFCTL_INTERNAL_FORCE_CFGREM)
  815. {
  816. DbgPrint((TRC_STATE,"The upper layer directs this config to be deleted => obey"));
  817. bConnectOK = FALSE;
  818. }
  819. // .. but if the configuration we just plumbed is saying we need to consider this a success
  820. // no matter what, do it so undoubtely
  821. else if (pConfig->dwCtlFlags & WZCCTL_INTERNAL_FORCE_CONNECT)
  822. {
  823. DbgPrint((TRC_STATE,"This config requests forced connect => obey (transition to {SN})"));
  824. bConnectOK = TRUE;
  825. }
  826. // if there is no special request and we were able to get the media status and we see the
  827. // media being connected, this means the configuration is successful.
  828. else if ((dwErr == ERROR_SUCCESS) && (pIntfContext->ulMediaState == MEDIA_STATE_CONNECTED))
  829. {
  830. DbgPrint((TRC_STATE,"Media is being connected in {SRs} =?=> transition to {SN}"));
  831. bConnectOK = TRUE;
  832. }
  833. // in all other cases the configuration is going to be deleted
  834. // go to the {SN} and {SC/SCk} based on the decision we took in bConnectOK
  835. if (bConnectOK)
  836. {
  837. // ...assume the configuration was successful (although we didn't get
  838. // the media connect event) and transition to {SN}
  839. pIntfContext->pfnStateHandler = StateNotifyFn;
  840. }
  841. else
  842. {
  843. UINT nFirstSelIdx; // first selection index following
  844. UINT nNSelIdx; // new selection index to use
  845. // if this is a forced delete, make sure we are not messing with old
  846. // WZCCTL_INTERNAL_FORCE_CONNECT flags - they could cause such a configuration to
  847. // be revived
  848. if (pIntfContext->dwCtlFlags & INTFCTL_INTERNAL_FORCE_CFGREM)
  849. {
  850. pConfig->dwCtlFlags &= ~WZCCTL_INTERNAL_FORCE_CONNECT;
  851. // since the upper layer is rejecting this configuration, make sure that
  852. // future iterations won't try it again, This bit helps to build/update
  853. // the BList (blocked list) when/if the state machine eventually gets
  854. // into the failed state {SF}.
  855. pConfig->dwCtlFlags |= WZCCTL_INTERNAL_BLOCKED;
  856. }
  857. // if this is an Adhoc network that just happened to fail, keep it in the list
  858. // such that it will be retried one more time and when this happens it shall
  859. // be considered successful no matter what.
  860. // However this is to be done only if the upper layer is not asking explicitly for
  861. // this config to be deleted
  862. if (pConfig->InfrastructureMode == Ndis802_11IBSS &&
  863. !(pConfig->dwCtlFlags & WZCCTL_INTERNAL_FORCE_CONNECT) &&
  864. !(pIntfContext->dwCtlFlags & INTFCTL_INTERNAL_FORCE_CFGREM))
  865. {
  866. //Record current state into logging DB
  867. DbLogWzcInfo(WZCSVC_SM_STATE_CFGSKIP, pIntfContext,
  868. DbLogFmtSSID(0, &(pConfig->Ssid)));
  869. pConfig->dwCtlFlags |= WZCCTL_INTERNAL_FORCE_CONNECT;
  870. }
  871. else
  872. {
  873. //Record current state into logging DB
  874. DbLogWzcInfo(WZCSVC_SM_STATE_CFGREMOVE, pIntfContext,
  875. DbLogFmtSSID(0, &(pConfig->Ssid)));
  876. }
  877. // mark this configuration as "bad"
  878. pConfig->dwCtlFlags |= WZCCTL_INTERNAL_DELETED;
  879. // regardless this was a forced or non-forced removal, reset the "force" control bit
  880. pIntfContext->dwCtlFlags &= ~INTFCTL_INTERNAL_FORCE_CFGREM;
  881. // if we just fail a "one time configuration", force a fresh beginning.
  882. // and remove the "one time" flag as we're getting out of this mode
  883. if (pIntfContext->dwCtlFlags & INTFCTL_INTERNAL_ONE_TIME)
  884. {
  885. DbgPrint((TRC_STATE,"Dropping a \"one time\" configuration"));
  886. pIntfContext->pwzcSList->Index = (pIntfContext->pwzcSList->NumberOfItems - 1);
  887. pIntfContext->dwCtlFlags &= ~INTFCTL_INTERNAL_ONE_TIME;
  888. }
  889. // scan for the next configuration that has not been marked "bad" yet
  890. for (nNSelIdx = (pIntfContext->pwzcSList->Index + 1) % pIntfContext->pwzcSList->NumberOfItems;
  891. nNSelIdx != pIntfContext->pwzcSList->Index;
  892. nNSelIdx = (nNSelIdx + 1) % pIntfContext->pwzcSList->NumberOfItems)
  893. {
  894. pConfig = &(pIntfContext->pwzcSList->Config[nNSelIdx]);
  895. if (!(pConfig->dwCtlFlags & WZCCTL_INTERNAL_DELETED))
  896. break;
  897. }
  898. // if we couldn't find any better candidate ...
  899. if (pConfig->dwCtlFlags & WZCCTL_INTERNAL_DELETED)
  900. {
  901. BOOL bFoundCandidate = FALSE;
  902. DbgPrint((TRC_STATE,"Went through all configs. Reviving now failed Adhocs."));
  903. // revive the adhocs that failed previously
  904. // This means that we reset the WZCCTL_INTERNAL_DELETED flag from all the configurations
  905. // having the WZCCTL_INTERNAL_FORCE_CONNECT flag set, and we let the latter untouched.
  906. // Because of this flag we'll actually consider the configuration to be successful when
  907. // it will be plumbed again. From that point on, it will be only the upper layer who will
  908. // be able to delete it again, and it is then when the WZCCTL_INTERNAL_FORCE_CONNECT
  909. // gets reset.
  910. for (nNSelIdx = 0; nNSelIdx < pIntfContext->pwzcSList->NumberOfItems; nNSelIdx++)
  911. {
  912. pConfig = &(pIntfContext->pwzcSList->Config[nNSelIdx]);
  913. if (pConfig->dwCtlFlags & WZCCTL_INTERNAL_FORCE_CONNECT)
  914. {
  915. DbgPrint((TRC_STATE,"Reviving configuration %d.", nNSelIdx));
  916. pConfig->dwCtlFlags &= ~WZCCTL_INTERNAL_DELETED;
  917. // the first configuration in this position is the candidate we were looking for
  918. if (!bFoundCandidate)
  919. {
  920. pIntfContext->pwzcSList->Index = nNSelIdx;
  921. bFoundCandidate = TRUE;
  922. }
  923. }
  924. }
  925. // if !bFoundCandidate, the configuration currently pointed by the pwzcSList->Index has
  926. // the "deleted" bit on. This will make {SIter} to jump directly to {SF}.
  927. }
  928. else
  929. {
  930. // if we could find another candidate, set the index to point it
  931. pIntfContext->pwzcSList->Index = nNSelIdx;
  932. }
  933. // transition automatically to {SIter} state
  934. pIntfContext->pfnStateHandler = StateIterateFn;
  935. }
  936. dwErr = ERROR_CONTINUE;
  937. DbgPrint((TRC_TRACK|TRC_STATE,"StateCfgRemoveFn]=%d", dwErr));
  938. return dwErr;
  939. }
  940. //-----------------------------------------------------------
  941. // StateCfgPreserveFn: Handler for the {SPs} state
  942. DWORD
  943. StateCfgPreserveFn(
  944. PINTF_CONTEXT pIntfContext)
  945. {
  946. DWORD dwErr = ERROR_SUCCESS;
  947. PWZC_WLAN_CONFIG pConfig;
  948. UINT nNSelIdx;
  949. UINT i;
  950. DbgPrint((TRC_TRACK|TRC_STATE,"[StateCfgPreserveFn(0x%p)", pIntfContext));
  951. DbgAssert((pIntfContext != NULL,"Invalid NULL context in {SPs} state"));
  952. DbgAssert((pIntfContext->pwzcSList != NULL, "Invalid null selection list in {SPs} state"));
  953. DbgAssert((pIntfContext->pwzcSList->Index < pIntfContext->pwzcSList->NumberOfItems, "Invalid selection index in {SPs} state"));
  954. pConfig = &(pIntfContext->pwzcSList->Config[pIntfContext->pwzcSList->Index]);
  955. //Record current state into logging DB
  956. DbLogWzcInfo(WZCSVC_SM_STATE_CFGPRESERVE, pIntfContext,
  957. DbLogFmtSSID(0, &(pConfig->Ssid)));
  958. // if we just skip a "one time configuration", then don't move the pointer out of it.
  959. // Basically we'll retry the same configuration over and over again until (if it completly
  960. // fails) it is removed from the selection list by the upper layer (802.1x).
  961. if (!(pIntfContext->dwCtlFlags & INTFCTL_INTERNAL_ONE_TIME))
  962. {
  963. // scan for the next configuration which has not been marked "bad" yet
  964. for (i = 0, nNSelIdx = (pIntfContext->pwzcSList->Index + 1) % pIntfContext->pwzcSList->NumberOfItems;
  965. i < pIntfContext->pwzcSList->NumberOfItems;
  966. i++, nNSelIdx = (nNSelIdx + 1) % pIntfContext->pwzcSList->NumberOfItems)
  967. {
  968. pConfig = &(pIntfContext->pwzcSList->Config[nNSelIdx]);
  969. if (!(pConfig->dwCtlFlags & WZCCTL_INTERNAL_DELETED))
  970. break;
  971. }
  972. // if we couldn't find any, clear the selection list and go back to
  973. // {SIter}. It will transition consequently to {SF}
  974. if (i == pIntfContext->pwzcSList->NumberOfItems)
  975. {
  976. WzcCleanupWzcList(pIntfContext->pwzcSList);
  977. pIntfContext->pwzcSList = NULL;
  978. }
  979. else
  980. {
  981. // if we could find another candidate, set the index to point it
  982. pIntfContext->pwzcSList->Index = nNSelIdx;
  983. }
  984. }
  985. pIntfContext->pfnStateHandler = StateIterateFn;
  986. dwErr = ERROR_CONTINUE;
  987. DbgPrint((TRC_TRACK|TRC_STATE,"StateCfgPreserveFn]=%d", dwErr));
  988. return dwErr;
  989. }
  990. //-----------------------------------------------------------
  991. // StateCfgHardKeyFn: Handler for the {SCk} state
  992. DWORD
  993. StateCfgHardKeyFn(
  994. PINTF_CONTEXT pIntfContext)
  995. {
  996. DWORD dwErr = ERROR_SUCCESS;
  997. PWZC_WLAN_CONFIG pSConfig;
  998. DbgPrint((TRC_TRACK|TRC_STATE,"[StateCfgHardKeyFn(0x%p)", pIntfContext));
  999. DbgAssert((pIntfContext != NULL,"Invalid NULL context in {SCk} state"));
  1000. // get a pointer to the currently selected configuration
  1001. pSConfig = &(pIntfContext->pwzcSList->Config[pIntfContext->pwzcSList->Index]);
  1002. //Record current state into logging DB
  1003. DbLogWzcInfo(WZCSVC_SM_STATE_CFGHDKEY, pIntfContext,
  1004. DbLogFmtSSID(0, &(pSConfig->Ssid)));
  1005. TIMER_SET(pIntfContext, TMMS_Tc, dwErr);
  1006. DbgPrint((TRC_TRACK|TRC_STATE,"StateCfgHardKeyFn]=%d", dwErr));
  1007. return dwErr;
  1008. }
  1009. //-----------------------------------------------------------
  1010. // StateNotifyFn: Handler for the {SN} state
  1011. DWORD
  1012. StateNotifyFn(
  1013. PINTF_CONTEXT pIntfContext)
  1014. {
  1015. DWORD dwErr = ERROR_SUCCESS;
  1016. PWZC_WLAN_CONFIG pSConfig;
  1017. PWZC_DEVICE_NOTIF pwzcNotif;
  1018. DWORD i;
  1019. DbgPrint((TRC_TRACK|TRC_STATE,"[StateNotifyFn(0x%p)", pIntfContext));
  1020. DbgAssert((pIntfContext != NULL,"Invalid NULL context in {SN} state"));
  1021. DbgAssert((pIntfContext->pwzcSList != NULL, "Invalid null selection list in {SN} state"));
  1022. DbgAssert((pIntfContext->pwzcSList->Index < pIntfContext->pwzcSList->NumberOfItems, "Invalid selection index in {SN} state"));
  1023. // we have a valid 802.11 config, so the ncstatus should read "connected"
  1024. pIntfContext->ncStatus = NCS_CONNECTED;
  1025. // notify netman about the ncstatus change (no need to check whether the
  1026. // service is enabled or not - it is enabled, otherwise we won't be in this state)
  1027. WzcNetmanNotify(pIntfContext);
  1028. // get a pointer to the currently selected configuration
  1029. pSConfig = &(pIntfContext->pwzcSList->Config[pIntfContext->pwzcSList->Index]);
  1030. // get the BSSID to which we're associated.
  1031. // if the BSSID was successfully retrieved then use this to generate the initial
  1032. // dynamic session keys. LstGenInitialSessionKeys is successfull only if the current
  1033. // configuration allows (association is successful and there is a user-provided wep key)
  1034. dwErr = DevioRefreshIntfOIDs(pIntfContext, INTF_BSSID, NULL);
  1035. //Record current state into logging DB as the first thing.
  1036. DbLogWzcInfo(WZCSVC_SM_STATE_NOTIFY, pIntfContext,
  1037. DbLogFmtSSID(0, &(pSConfig->Ssid)),
  1038. DbLogFmtBSSID(1, pIntfContext->wzcCurrent.MacAddress));
  1039. // now check if there was any error getting the BSSID - if it was, log it.
  1040. // otherwise go on and generate the initial session keys.
  1041. if (dwErr != ERROR_SUCCESS)
  1042. {
  1043. // if there was any error getting the BSSID, log the error here
  1044. DbLogWzcError(WZCSVC_ERR_QUERRY_BSSID, pIntfContext, dwErr);
  1045. }
  1046. else
  1047. {
  1048. dwErr = LstGenInitialSessionKeys(pIntfContext);
  1049. // if there was any error setting the initial session keys, log it here
  1050. if (dwErr != ERROR_SUCCESS)
  1051. DbLogWzcError(WZCSVC_ERR_GEN_SESSION_KEYS, pIntfContext, dwErr);
  1052. }
  1053. // no error is critical enough so far to justify stopping the state machine.
  1054. // .. so reset it to "success"
  1055. dwErr = ERROR_SUCCESS;
  1056. // allocate memory for a WZC_CONFIG_NOTIF object large enough to include the interface's GUID
  1057. pwzcNotif = MemCAlloc(sizeof(WZC_DEVICE_NOTIF) + wcslen(pIntfContext->wszGuid)*sizeof(WCHAR));
  1058. if (pwzcNotif == NULL)
  1059. {
  1060. DbgAssert((FALSE, "Out of memory on allocating the WZC_DEVICE_NOTIF object"));
  1061. dwErr = GetLastError();
  1062. goto exit;
  1063. }
  1064. // initialize the WZC_CONFIG_NOTIF
  1065. // this is a WZCNOTIF_WZC_CONNECT event that is going up
  1066. pwzcNotif->dwEventType = WZCNOTIF_WZC_CONNECT;
  1067. pwzcNotif->wzcConfig.dwSessionHdl = pIntfContext->dwSessionHandle;
  1068. wcscpy(pwzcNotif->wzcConfig.wszGuid, pIntfContext->wszGuid);
  1069. memcpy(&pwzcNotif->wzcConfig.ndSSID, &pSConfig->Ssid, sizeof(NDIS_802_11_SSID));
  1070. // copy into the notification the user data associated with the current config
  1071. pwzcNotif->wzcConfig.rdEventData.dwDataLen = pSConfig->rdUserData.dwDataLen;
  1072. if (pwzcNotif->wzcConfig.rdEventData.dwDataLen > 0)
  1073. {
  1074. pwzcNotif->wzcConfig.rdEventData.pData = MemCAlloc(pSConfig->rdUserData.dwDataLen);
  1075. if (pwzcNotif->wzcConfig.rdEventData.pData == NULL)
  1076. {
  1077. DbgAssert((FALSE, "Out of memory on allocating the WZC_CONFIG_NOTIF user data"));
  1078. dwErr = GetLastError();
  1079. MemFree(pwzcNotif);
  1080. goto exit;
  1081. }
  1082. memcpy(pwzcNotif->wzcConfig.rdEventData.pData,
  1083. pSConfig->rdUserData.pData,
  1084. pSConfig->rdUserData.dwDataLen);
  1085. }
  1086. // Asynchronously call into the upper level app (802.1x)
  1087. // notifying that the selected 802.11 configuration is successful.
  1088. DbgPrint((TRC_NOTIF, "Sending WZCNOTIF_WZC_CONNECT notification (SessHdl %d)",
  1089. pIntfContext->dwSessionHandle));
  1090. InterlockedIncrement(&g_nThreads);
  1091. if (!QueueUserWorkItem((LPTHREAD_START_ROUTINE)WZCWrkWzcSendNotif,
  1092. (LPVOID)pwzcNotif,
  1093. WT_EXECUTELONGFUNCTION))
  1094. {
  1095. DbgAssert((FALSE, "Can't create WZCWrkWzcSendNotif worker thread"));
  1096. dwErr = GetLastError();
  1097. InterlockedDecrement(&g_nThreads);
  1098. MemFree(pwzcNotif->wzcConfig.rdEventData.pData);
  1099. MemFree(pwzcNotif);
  1100. goto exit;
  1101. }
  1102. DbgPrint((TRC_STATE,"Requesting the refresh of the DHCP lease"));
  1103. // once the configuration has been set up correctly, Zero Conf needs to trigger
  1104. // DHCP client one more time asking for the lease to be refreshed. It needs to do so
  1105. // because it is not guaranteed that a media connect notification will be generated
  1106. // and hence DHCP client might have no knowledge about the network being brought up
  1107. // back. Note also the call below is (and should be) asynchronous
  1108. DhcpStaticRefreshParams(pIntfContext->wszGuid);
  1109. // at this point, set back the "signal" control bit since right now we're in the
  1110. // successful case! On next failure (whenever that might be) the signal must not
  1111. // be suppressed.
  1112. pIntfContext->dwCtlFlags |= INTFCTL_INTERNAL_SIGNAL;
  1113. // also, mark this context has been sent up the notification to 802.1x.
  1114. // If we're here because of a PnP event, then this will tell to the notification
  1115. // handler to not forward the notification up since it would be totally redundant.
  1116. // If this is not PnP event, this bit will be cleaned up by whoever called StateDispatchEvent.
  1117. pIntfContext->dwCtlFlags |= INTFCTL_INTERNAL_BLK_MEDIACONN;
  1118. // automatic transition to either {SCk} or {SC} depending whether the remote guy
  1119. // is requiring privacy and we rely the privacy on a faked key
  1120. if (pSConfig->Privacy && pIntfContext->dwCtlFlags & INTFCTL_INTERNAL_FAKE_WKEY)
  1121. pIntfContext->pfnStateHandler = StateCfgHardKeyFn;
  1122. else
  1123. pIntfContext->pfnStateHandler = StateConfiguredFn;
  1124. dwErr = ERROR_CONTINUE;
  1125. exit:
  1126. DbgPrint((TRC_TRACK|TRC_STATE,"StateNotifyFn]=%d", dwErr));
  1127. return dwErr;
  1128. }