Leaked source code of windows server 2003
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.

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