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.

1500 lines
40 KiB

  1. /*++
  2. Copyright (c) 1997 Microsoft Corporation
  3. Module Name:
  4. fsm.cxx
  5. Abstract:
  6. Contains CFsm class implementation
  7. Contents:
  8. ContainingFsm
  9. DoFsm
  10. CFsm::CFsm
  11. CFsm::~CFsm
  12. CFsm::Push
  13. CFsm::Pop
  14. CFsm::QueueWorkItem
  15. CFsm::RunWorkItem
  16. CFsm::Run
  17. [CFsm::MapType]
  18. [CFsm::StateName]
  19. Author:
  20. Richard L Firth (rfirth) 11-Apr-1997
  21. Environment:
  22. Win32 user-mode DLL
  23. Revision History:
  24. 11-Apr-1997 rfirth
  25. Created
  26. --*/
  27. #include <wininetp.h>
  28. #include <perfdiag.hxx>
  29. #ifdef USE_DINARES_FSM_ALLOC_CACHE
  30. CCritSec FsmAllocCritSec;
  31. void * FsmAllocList;
  32. size_t FsmAllocSize;
  33. #endif
  34. #if INET_DEBUG
  35. struct { int size; char* name; } class_sizes[] = {
  36. sizeof(CFsm), "CFsm",
  37. sizeof(CFsm_ResolveHost), "CFsm_ResolveHost",
  38. sizeof(CFsm_InternetParseUrl), "CFsm_InternetParseUrl",
  39. sizeof(CFsm_InternetQueryDataAvailable), "CFsm_InternetQueryDataAvailable",
  40. sizeof(CFsm_InternetWriteFile), "CFsm_InternetWriteFile",
  41. sizeof(CFsm_InternetReadFile), "CFsm_InternetReadFile",
  42. sizeof(CFsm_SocketConnect), "CFsm_SocketConnect",
  43. sizeof(CFsm_SocketSend), "CFsm_SocketSend",
  44. sizeof(CFsm_SocketReceive), "CFsm_SocketReceive",
  45. //sizeof(CFsm_SocketDataAvailable), "CFsm_SocketDataAvailable",
  46. sizeof(CFsm_SecureConnect), "CFsm_SecureConnect",
  47. sizeof(CFsm_SecureHandshake), "CFsm_SecureHandshake",
  48. sizeof(CFsm_SecureNegotiate), "CFsm_SecureNegotiate",
  49. sizeof(CFsm_NegotiateLoop), "CFsm_NegotiateLoop",
  50. sizeof(CFsm_SecureSend), "CFsm_SecureSend",
  51. sizeof(CFsm_SecureReceive), "CFsm_SecureReceive",
  52. sizeof(CFsm_GetConnection), "CFsm_GetConnection",
  53. sizeof(CFsm_HttpSendRequest), "CFsm_HttpSendRequest",
  54. sizeof(CFsm_MakeConnection), "CFsm_MakeConnection",
  55. sizeof(CFsm_OpenConnection), "CFsm_OpenConnection",
  56. sizeof(CFsm_OpenProxyTunnel), "CFsm_OpenProxyTunnel",
  57. sizeof(CFsm_SendRequest), "CFsm_SendRequest",
  58. sizeof(CFsm_ReceiveResponse), "CFsm_ReceiveResponse",
  59. sizeof(CFsm_HttpReadData), "CFsm_HttpReadData",
  60. sizeof(CFsm_HttpWriteData), "CFsm_HttpWriteData",
  61. sizeof(CFsm_ReadData), "CFsm_ReadData",
  62. sizeof(CFsm_HttpQueryAvailable), "CFsm_HttpQueryAvailable",
  63. sizeof(CFsm_DrainResponse), "CFsm_DrainResponse",
  64. sizeof(CFsm_Redirect), "CFsm_Redirect",
  65. sizeof(CFsm_ReadLoop), "CFsm_ReadLoop",
  66. sizeof(CFsm_ParseHttpUrl), "CFsm_ParseHttpUrl",
  67. sizeof(CFsm_OpenUrl), "CFsm_OpenUrl",
  68. sizeof(CFsm_ParseUrlForHttp), "CFsm_ParseUrlForHttp",
  69. sizeof(CFsm_ReadFile), "CFsm_ReadFile",
  70. sizeof(CFsm_ReadFileEx), "CFsm_ReadFileEx",
  71. //sizeof(CFsm_WriteFile), "CFsm_WriteFile",
  72. sizeof(CFsm_BackgroundTask), "CFsm_BackgroundTask",
  73. sizeof(CFsm_QueryAvailable), "CFsm_QueryAvailable"
  74. };
  75. void dump_class_sizes() {
  76. for (int i = 0; i < ARRAY_ELEMENTS(class_sizes); ++i) {
  77. DEBUG_PRINT(ASYNC,INFO,("%s = %d\n", class_sizes[i].name, class_sizes[i].size));
  78. }
  79. }
  80. #endif
  81. //
  82. // functions
  83. //
  84. //
  85. // This is Dinarte's experiement for reducing Mem alloc on
  86. // creating FSMs.
  87. //
  88. #ifdef USE_DINARES_FSM_ALLOC_CACHE
  89. VOID
  90. FsmInitialize(
  91. VOID
  92. )
  93. /*++
  94. Routine Description:
  95. Performs initialization required by functions in this module
  96. Arguments:
  97. None.
  98. Return Value:
  99. DWORD
  100. Success - ERROR_SUCCESS
  101. Failure - return code from LocalAlloc
  102. --*/
  103. {
  104. DEBUG_ENTER((DBG_ASYNC,
  105. None,
  106. "FsmInitialize",
  107. NULL
  108. ));
  109. FsmAllocCritSec.Init();
  110. FsmAllocSize = sizeof(CFsm);
  111. if (FsmAllocSize < sizeof(CFsm_ResolveHost))
  112. FsmAllocSize = sizeof(CFsm_ResolveHost);
  113. if (FsmAllocSize < sizeof(CFsm_SocketConnect))
  114. FsmAllocSize = sizeof(CFsm_SocketConnect);
  115. if (FsmAllocSize < sizeof(CFsm_SocketSend))
  116. FsmAllocSize = sizeof(CFsm_SocketSend);
  117. if (FsmAllocSize < sizeof(CFsm_SocketReceive))
  118. FsmAllocSize = sizeof(CFsm_SocketReceive);
  119. if (FsmAllocSize < sizeof(CFsm_SecureConnect))
  120. FsmAllocSize = sizeof(CFsm_SecureConnect);
  121. if (FsmAllocSize < sizeof(CFsm_SecureHandshake))
  122. FsmAllocSize = sizeof(CFsm_SecureHandshake);
  123. if (FsmAllocSize < sizeof(CFsm_SecureNegotiate))
  124. FsmAllocSize = sizeof(CFsm_SecureNegotiate);
  125. if (FsmAllocSize < sizeof(CFsm_NegotiateLoop))
  126. FsmAllocSize = sizeof(CFsm_NegotiateLoop);
  127. if (FsmAllocSize < sizeof(CFsm_SecureSend))
  128. FsmAllocSize = sizeof(CFsm_SecureSend);
  129. if (FsmAllocSize < sizeof(CFsm_SecureReceive))
  130. FsmAllocSize = sizeof(CFsm_SecureReceive);
  131. if (FsmAllocSize < sizeof(CFsm_GetConnection))
  132. FsmAllocSize = sizeof(CFsm_GetConnection);
  133. if (FsmAllocSize < sizeof(CFsm_HttpSendRequest))
  134. FsmAllocSize = sizeof(CFsm_HttpSendRequest);
  135. if (FsmAllocSize < sizeof(CFsm_MakeConnection))
  136. FsmAllocSize = sizeof(CFsm_MakeConnection);
  137. if (FsmAllocSize < sizeof(CFsm_OpenConnection))
  138. FsmAllocSize = sizeof(CFsm_OpenConnection);
  139. if (FsmAllocSize < sizeof(CFsm_OpenProxyTunnel))
  140. FsmAllocSize = sizeof(CFsm_OpenProxyTunnel);
  141. if (FsmAllocSize < sizeof(CFsm_SendRequest))
  142. FsmAllocSize = sizeof(CFsm_SendRequest);
  143. if (FsmAllocSize < sizeof(CFsm_ReceiveResponse))
  144. FsmAllocSize = sizeof(CFsm_ReceiveResponse);
  145. if (FsmAllocSize < sizeof(CFsm_HttpReadData))
  146. FsmAllocSize = sizeof(CFsm_HttpReadData);
  147. if (FsmAllocSize < sizeof(CFsm_HttpWriteData))
  148. FsmAllocSize = sizeof(CFsm_HttpWriteData);
  149. if (FsmAllocSize < sizeof(CFsm_ReadData))
  150. FsmAllocSize = sizeof(CFsm_ReadData);
  151. if (FsmAllocSize < sizeof(CFsm_HttpQueryAvailable))
  152. FsmAllocSize = sizeof(CFsm_HttpQueryAvailable);
  153. if (FsmAllocSize < sizeof(CFsm_DrainResponse))
  154. FsmAllocSize = sizeof(CFsm_DrainResponse);
  155. if (FsmAllocSize < sizeof(CFsm_Redirect))
  156. FsmAllocSize = sizeof(CFsm_Redirect);
  157. if (FsmAllocSize < sizeof(CFsm_ReadLoop))
  158. FsmAllocSize = sizeof(CFsm_ReadLoop);
  159. if (FsmAllocSize < sizeof(CFsm_ParseHttpUrl))
  160. FsmAllocSize = sizeof(CFsm_ParseHttpUrl);
  161. if (FsmAllocSize < sizeof(CFsm_OpenUrl))
  162. FsmAllocSize = sizeof(CFsm_OpenUrl);
  163. if (FsmAllocSize < sizeof(CFsm_ParseUrlForHttp))
  164. FsmAllocSize = sizeof(CFsm_ParseUrlForHttp);
  165. if (FsmAllocSize < sizeof(CFsm_ReadFile))
  166. FsmAllocSize = sizeof(CFsm_ReadFile);
  167. if (FsmAllocSize < sizeof(CFsm_ReadFileEx))
  168. FsmAllocSize = sizeof(CFsm_ReadFileEx);
  169. if (FsmAllocSize < sizeof(CFsm_QueryAvailable))
  170. FsmAllocSize = sizeof(CFsm_QueryAvailable);
  171. //
  172. // Pre-allocate a pool of state-machines for locality of reference
  173. //
  174. for (int cPreAlloc = 8192 / FsmAllocSize; cPreAlloc > 0; --cPreAlloc)
  175. {
  176. void * pFsm = (void *)ALLOCATE_FIXED_MEMORY(FsmAllocSize);
  177. if (pFsm == NULL)
  178. break;
  179. *(void **)pFsm = FsmAllocList;
  180. FsmAllocList = pFsm;
  181. }
  182. }
  183. VOID
  184. FsmTerminate(
  185. VOID
  186. )
  187. /*++
  188. Routine Description:
  189. Obverse of FsmInitialize - frees any system resources allocated by
  190. FsmInitialize
  191. Arguments:
  192. None.
  193. Return Value:
  194. None.
  195. --*/
  196. {
  197. DEBUG_ENTER((DBG_ASYNC,
  198. None,
  199. "FsmTerminate",
  200. NULL
  201. ));
  202. //
  203. // there shouldn't be any other threads active when this function is called
  204. // but we'll grab the critical section anyway, just to make sure
  205. //
  206. if (FsmAllocCritSec.Lock())
  207. {
  208. while (FsmAllocList)
  209. {
  210. void * pFsm = FsmAllocList;
  211. FsmAllocList = *(void **)pFsm;
  212. FREE_MEMORY((HLOCAL)pFsm);
  213. }
  214. FsmAllocCritSec.Unlock();
  215. }
  216. //
  217. // delete the critical section
  218. //
  219. FsmAllocCritSec.FreeLock();
  220. DEBUG_LEAVE(0);
  221. }
  222. #endif
  223. CFsm *
  224. ContainingFsm(
  225. IN LPVOID lpAddress
  226. )
  227. /*++
  228. Routine Description:
  229. Returns address of start of CFsm object, including vtable
  230. Arguments:
  231. lpAddress - pointer to list inside CFsm object
  232. Return Value:
  233. CFsm * - pointer to start of object
  234. --*/
  235. {
  236. return CONTAINING_RECORD(lpAddress, CFsm, m_ListEntry);
  237. }
  238. //DWORD
  239. //RunAll(
  240. // VOID
  241. // )
  242. //{
  243. // DWORD error;
  244. // LPINTERNET_THREAD_INFO lpThreadInfo = InternetGetThreadInfo();
  245. //
  246. // if (lpThreadInfo != NULL) {
  247. // while (lpThreadInfo->Fsm != NULL) {
  248. // lpThreadInfo->Fsm->Run();
  249. // }
  250. // } else {
  251. // error = ERROR_WINHTTP_INTERNAL_ERROR;
  252. // }
  253. // return error;
  254. //}
  255. DWORD
  256. DoFsm(
  257. IN CFsm * pFsm
  258. )
  259. /*++
  260. Routine Description:
  261. Common FSM run processing
  262. Arguments:
  263. pFsm - FSM to run (maybe NULL if new failed)
  264. Return Value:
  265. DWORD - return code from running FSM
  266. --*/
  267. {
  268. DEBUG_ENTER((DBG_ASYNC,
  269. Dword,
  270. "DoFsm",
  271. "%#x (%s)",
  272. pFsm,
  273. pFsm->MapType()
  274. ));
  275. DWORD error;
  276. LPINTERNET_THREAD_INFO lpThreadInfo = InternetGetThreadInfo();
  277. INET_ASSERT(lpThreadInfo != NULL);
  278. if (lpThreadInfo != NULL) {
  279. if (pFsm != NULL) {
  280. error = pFsm->GetError();
  281. if (error == ERROR_SUCCESS) {
  282. error = pFsm->Run(lpThreadInfo, NULL, NULL);
  283. } else {
  284. INET_ASSERT(FALSE);
  285. delete pFsm;
  286. }
  287. } else {
  288. error = ERROR_NOT_ENOUGH_MEMORY;
  289. }
  290. } else {
  291. error = ERROR_WINHTTP_INTERNAL_ERROR;
  292. }
  293. DEBUG_LEAVE(error);
  294. return error;
  295. }
  296. DWORD
  297. DoAsyncFsm(
  298. IN CFsm * pFsm,
  299. IN HTTP_REQUEST_HANDLE_OBJECT *pRequest
  300. )
  301. /*++
  302. Routine Description:
  303. Common FSM run processing for asynchronous requests which
  304. are starting new fsm chains.
  305. Arguments:
  306. pFsm - FSM to run (maybe NULL if new failed)
  307. pRequest - When not NULL, the FSM will be checked and placed
  308. into a blocked queue if an async work item on the
  309. request item is already in progress. It's assumed
  310. this is only called for async request objects.
  311. Return Value:
  312. DWORD - return code from running FSM
  313. --*/
  314. {
  315. DEBUG_ENTER((DBG_ASYNC,
  316. Dword,
  317. "DoAsyncFsm",
  318. "%#x (%s)",
  319. pFsm,
  320. pFsm->MapType()
  321. ));
  322. DWORD error;
  323. LPINTERNET_THREAD_INFO lpThreadInfo = InternetGetThreadInfo();
  324. INET_ASSERT(lpThreadInfo != NULL);
  325. if (lpThreadInfo != NULL) {
  326. if (pFsm != NULL) {
  327. error = pFsm->GetError();
  328. if (error == ERROR_SUCCESS) {
  329. if (pRequest) {
  330. if (pRequest->LockAsync())
  331. {
  332. if (pRequest->IsWorkItemInProgress())
  333. {
  334. // Wait until current and blocked work items are finished.
  335. error = pRequest->BlockWorkItem(pFsm);
  336. if (error == ERROR_SUCCESS)
  337. {
  338. error = ERROR_IO_PENDING;
  339. }
  340. }
  341. else
  342. {
  343. pRequest->SetWorkItemInProgress(TRUE);
  344. pFsm->SetPushPop(TRUE);
  345. pFsm->Push();
  346. error = pFsm->QueueWorkItem();
  347. }
  348. pRequest->UnlockAsync();
  349. }
  350. else
  351. {
  352. delete pFsm;
  353. pFsm = NULL;
  354. error = ERROR_NOT_ENOUGH_MEMORY;
  355. }
  356. }
  357. else {
  358. error = pFsm->Run(lpThreadInfo, NULL, NULL);
  359. }
  360. } else {
  361. INET_ASSERT(FALSE);
  362. delete pFsm;
  363. }
  364. } else {
  365. error = ERROR_NOT_ENOUGH_MEMORY;
  366. }
  367. } else {
  368. error = ERROR_WINHTTP_INTERNAL_ERROR;
  369. }
  370. DEBUG_LEAVE(error);
  371. return error;
  372. }
  373. //
  374. // methods
  375. //
  376. //
  377. // This is Dinarte's experiement for reducing Mem alloc on
  378. // creating FSMs. Not used right now.
  379. //
  380. #ifdef USE_DINARES_FSM_ALLOC_CACHE
  381. void *
  382. CFsm::operator new(
  383. IN size_t Size
  384. )
  385. /*++
  386. Routine Description:
  387. Allocates memory for the new state-machine from a special memory pool.
  388. Arguments:
  389. Size - size of the state-machine
  390. Return Value:
  391. None.
  392. --*/
  393. {
  394. VOID * pFsm = NULL;
  395. if (FsmAllocCritSec.Lock())
  396. {
  397. // Only alloc from the list if we can synchronize access to it.
  398. pFsm = FsmAllocList;
  399. if (pFsm)
  400. {
  401. FsmAllocList = *(void **)pFsm;
  402. }
  403. FsmAllocCritSec.Unlock();
  404. }
  405. if (pFsm == NULL)
  406. {
  407. INET_ASSERT(Size <= FsmAllocSize);
  408. pFsm = (void *)ALLOCATE_FIXED_MEMORY(FsmAllocSize);
  409. }
  410. return(pFsm);
  411. }
  412. VOID
  413. CFsm::operator delete(
  414. IN VOID * pFsm
  415. )
  416. /*++
  417. Routine Description:
  418. Deallocates memory for the state-machine by adding it to a special
  419. memory pool.
  420. Arguments:
  421. pFsm - pointer to the state-machine
  422. Return Value:
  423. None.
  424. --*/
  425. {
  426. if (pFsm)
  427. {
  428. if (FsmAllocCritSec.Lock())
  429. {
  430. *(void **)pFsm = FsmAllocList;
  431. FsmAllocList = pFsm;
  432. FsmAllocCritSec.Unlock();
  433. }
  434. // else leak?
  435. }
  436. }
  437. #endif
  438. CFsm::CFsm(
  439. IN DWORD (* lpfnHandler)(CFsm *),
  440. IN LPVOID lpvContext,
  441. IN BOOL fPushPop /* = TRUE */
  442. ) : CPriorityListEntry(0)
  443. /*++
  444. Routine Description:
  445. CFsm constructor. This gets called many times since its the base of all the
  446. derived FSMs
  447. Arguments:
  448. lpfnHandler - state-machine handler
  449. lpvContext - callee context
  450. Return Value:
  451. None.
  452. --*/
  453. {
  454. #if INET_DEBUG
  455. static bool b = TRUE;
  456. if (b) {
  457. dump_class_sizes();
  458. b=FALSE;
  459. }
  460. #endif
  461. DEBUG_ENTER((DBG_OBJECTS,
  462. None,
  463. "CFsm::CFsm",
  464. "{%#x}",
  465. this
  466. ));
  467. INIT_FSM();
  468. m_lpThreadInfo = InternetGetThreadInfo();
  469. if (m_lpThreadInfo == NULL) {
  470. INET_ASSERT(m_lpThreadInfo != NULL);
  471. SetError(ERROR_WINHTTP_INTERNAL_ERROR);
  472. DEBUG_LEAVE(0);
  473. return;
  474. }
  475. m_hObject = m_lpThreadInfo->hObject;
  476. m_hObjectMapped = (INTERNET_HANDLE_OBJECT *)m_lpThreadInfo->hObjectMapped;
  477. m_dwContext = m_hObjectMapped->GetContext();
  478. m_dwMappedErrorCode = m_lpThreadInfo->dwMappedErrorCode;
  479. m_State = FSM_STATE_INIT;
  480. m_NextState = FSM_STATE_CONTINUE;
  481. m_FunctionState = FSM_STATE_BAD;
  482. m_lpfnHandler = lpfnHandler;
  483. m_lpvContext = lpvContext;
  484. SetError(ERROR_SUCCESS);
  485. m_bPushPop = fPushPop;
  486. if (fPushPop)
  487. Push();
  488. m_Hint = FSM_HINT_SLOW;
  489. m_Socket = INVALID_SOCKET;
  490. m_Action = FSM_ACTION_NONE;
  491. m_dwBlockId = 0;
  492. m_dwTimeout = INFINITE;
  493. m_fTimeoutWraps = FALSE;
  494. m_dwTimer = 0;
  495. m_bTimerStarted = FALSE;
  496. m_bIsBlockingFsm = FALSE;
  497. m_bIsApi = FALSE;
  498. m_ApiType = ApiType_None;
  499. m_dwApiData = 0;
  500. m_ApiResult.Handle = NULL;
  501. m_bHasTimeout = FALSE;
  502. m_bOnAsyncList = FALSE;
  503. DEBUG_LEAVE(0);
  504. }
  505. CFsm::~CFsm()
  506. /*++
  507. Routine Description:
  508. CFsm desctructor
  509. Arguments:
  510. None.
  511. Return Value:
  512. None.
  513. --*/
  514. {
  515. DEBUG_ENTER((DBG_OBJECTS,
  516. None,
  517. "CFsm::~CFsm",
  518. "{%#x}",
  519. this
  520. ));
  521. CHECK_FSM();
  522. CHECK_OWNED();
  523. if (m_bPushPop)
  524. Pop();
  525. #ifdef STRESS_BUG_DEBUG
  526. m_Link = (CFsm *) (DWORD_PTR) 0xFEFEFEFE;
  527. m_dwError = 0xFEFEFEFE;
  528. m_lpThreadInfo = (LPINTERNET_THREAD_INFO) (DWORD_PTR) 0xFEFEFEFE;
  529. m_dwContext = 0xFEFEFEFE;
  530. m_hObject = (HINTERNET)(DWORD_PTR) 0xFEFEFEFE;
  531. #endif
  532. DEBUG_LEAVE(0);
  533. }
  534. VOID
  535. CFsm::Push(
  536. VOID
  537. )
  538. /*++
  539. Routine Description:
  540. Adds this FSM to the head of the queue
  541. Arguments:
  542. None.
  543. Return Value:
  544. None.
  545. --*/
  546. {
  547. DEBUG_ENTER((DBG_ASYNC,
  548. None,
  549. "CFsm::Push",
  550. "{%#x (%s:%s)}",
  551. this,
  552. MapState(),
  553. MapFunctionState()
  554. ));
  555. CHECK_FSM();
  556. CHECK_UNOWNED();
  557. INET_ASSERT(m_lpThreadInfo != NULL);
  558. CHECK_INTERNET_THREAD_INFO(m_lpThreadInfo);
  559. m_Link = m_lpThreadInfo->Fsm;
  560. m_lpThreadInfo->Fsm = this;
  561. CHECK_FSM_OWNED(m_Link);
  562. RESET_FSM_OWNED(m_Link);
  563. DEBUG_PRINT(ASYNC,
  564. INFO,
  565. ("!!! FSM %#x unowned\n", m_Link
  566. ));
  567. SET_OWNED();
  568. DEBUG_PRINT(ASYNC,
  569. INFO,
  570. ("!!! FSM %#x owned by %#x\n",
  571. this,
  572. GetCurrentThreadId()
  573. ));
  574. DEBUG_LEAVE(0);
  575. }
  576. VOID
  577. CFsm::Pop(
  578. VOID
  579. )
  580. /*++
  581. Routine Description:
  582. Puts the next FSM (if any) at the head of the queue
  583. Arguments:
  584. None.
  585. Return Value:
  586. None.
  587. --*/
  588. {
  589. DEBUG_ENTER((DBG_ASYNC,
  590. None,
  591. "CFsm::Pop",
  592. "{%#x (%s:%s)}",
  593. this,
  594. MapState(),
  595. MapFunctionState()
  596. ));
  597. INET_ASSERT(m_lpThreadInfo != NULL);
  598. CHECK_INTERNET_THREAD_INFO(m_lpThreadInfo);
  599. CHECK_FSM();
  600. CHECK_OWNED();
  601. CHECK_FSM_UNOWNED(m_Link);
  602. CFsm * pNextFsm = m_Link;
  603. m_lpThreadInfo->Fsm = pNextFsm;
  604. SET_FSM_OWNED(pNextFsm);
  605. DEBUG_PRINT(ASYNC,
  606. INFO,
  607. ("!!! FSM %#x owned by %#x\n",
  608. pNextFsm,
  609. GetCurrentThreadId()
  610. ));
  611. if (pNextFsm != NULL) {
  612. pNextFsm->SetState(pNextFsm->GetNextState());
  613. pNextFsm->SetError(GetError());
  614. DEBUG_PRINT(ASYNC,
  615. INFO,
  616. ("next FSM %#x (%s), state %s, function-state %s\n",
  617. pNextFsm,
  618. pNextFsm->MapType(),
  619. pNextFsm->MapState(),
  620. pNextFsm->MapFunctionState()
  621. ));
  622. } else {
  623. DEBUG_PRINT(ASYNC,
  624. INFO,
  625. ("last FSM\n"
  626. ));
  627. }
  628. DEBUG_LEAVE(0);
  629. }
  630. DWORD
  631. CFsm::QueueWorkItem(
  632. VOID
  633. )
  634. /*++
  635. Routine Description:
  636. Queues this FSM to worker thread for processing. Worker thread callback is
  637. CFsm::RunWorkItem
  638. Arguments:
  639. None.
  640. Return Value:
  641. DWORD
  642. Success - ERROR_SUCCESS
  643. Failure - return code from SHQueueUserWorkItem
  644. --*/
  645. {
  646. DEBUG_ENTER((DBG_ASYNC,
  647. Dword,
  648. "CFsm::QueueWorkItem",
  649. "{%#x [%s, socket %#x, block id %#x, timeout %#x, error %d, state %s:%s]}",
  650. this,
  651. MapType(),
  652. GetSocket(),
  653. GetBlockId(),
  654. GetTimeout(),
  655. GetError(),
  656. MapState(),
  657. MapFunctionState()
  658. ));
  659. DWORD error = ERROR_IO_PENDING;
  660. RESET_OWNED();
  661. DEBUG_PRINT(SOCKETS,
  662. INFO,
  663. ("Posting IO completion with 0x%x\n",
  664. this
  665. ));
  666. DEBUG_ENTER((DBG_API,
  667. Bool,
  668. "***PostQueuedCompletionStatus",
  669. "(hComp)%#x, (# bytes)%#x, (completionkey)%#x, (overlapped)%#x",
  670. g_hCompletionPort,
  671. COMPLETION_BYTES_CUSTOM,
  672. this,
  673. g_lpCustomOverlapped
  674. ));
  675. if (PostQueuedCompletionStatus(g_hCompletionPort,
  676. COMPLETION_BYTES_CUSTOM,
  677. ULONG_PTR (this),
  678. g_lpCustomOverlapped
  679. ))
  680. {
  681. DEBUG_LEAVE(TRUE);
  682. #if INET_DEBUG
  683. InterlockedIncrement(&g_cCustomCompletions);
  684. #endif
  685. LPINTERNET_THREAD_INFO lpThreadInfo = InternetGetThreadInfo();
  686. INET_ASSERT(lpThreadInfo != NULL);
  687. if (lpThreadInfo != NULL)
  688. lpThreadInfo->Fsm = NULL;
  689. }
  690. else
  691. {
  692. DEBUG_LEAVE(FALSE);
  693. error = GetLastError();
  694. }
  695. INET_ASSERT(error == ERROR_IO_PENDING);
  696. DEBUG_LEAVE(error);
  697. return error;
  698. }
  699. DWORD
  700. CFsm::RunWorkItem(
  701. IN CFsm * pFsm
  702. )
  703. /*++
  704. Routine Description:
  705. Run the current work item to the next block state or completion. This
  706. (class static) function is called in the context of a system thread pool
  707. callback function
  708. Arguments:
  709. pFsm - pointer to FSM to run
  710. Return Value:
  711. DWORD
  712. Success - ERROR_SUCCESS
  713. Failure - ERROR_WINHTTP_INTERNAL_ERROR
  714. --*/
  715. {
  716. LPINTERNET_THREAD_INFO lpThreadInfo = InternetGetThreadInfo();
  717. INET_ASSERT(lpThreadInfo != NULL);
  718. if (lpThreadInfo == NULL) {
  719. INET_ASSERT(FALSE);
  720. return ERROR_WINHTTP_INTERNAL_ERROR;
  721. }
  722. DEBUG_ENTER((DBG_ASYNC,
  723. Dword,
  724. "RunWorkItem",
  725. "%#x",
  726. pFsm
  727. ));
  728. PERF_ENTER(Worker);
  729. DWORD error;
  730. DEBUG_PRINT(ASYNC,
  731. INFO,
  732. ("%s Fsm %#x: socket %#x, block id %#x, timeout %#x, error %d\n",
  733. pFsm->MapType(),
  734. pFsm,
  735. pFsm->GetSocket(),
  736. pFsm->GetBlockId(),
  737. pFsm->GetTimeout(),
  738. pFsm->GetError()
  739. ));
  740. INTERNET_HANDLE_BASE *pSession = (INTERNET_HANDLE_BASE *)(pFsm->GetMappedHandle());
  741. HANDLE ThreadToken = pSession->GetThreadToken();
  742. HANDLE ThreadHandle = GetCurrentThread();
  743. if (ThreadToken)
  744. {
  745. if (::SetThreadToken(&ThreadHandle,
  746. &ThreadToken) == FALSE)
  747. {
  748. ThreadToken = 0;
  749. }
  750. }
  751. while (TRUE) {
  752. INET_ASSERT(pFsm != NULL);
  753. BOOL bIsApi = pFsm->IsApi();
  754. API_TYPE apiType = pFsm->GetApiType();
  755. FSM_TYPE fsmType = pFsm->GetType();
  756. lpThreadInfo->Fsm = pFsm;
  757. SET_FSM_OWNED(pFsm);
  758. // We could pass back a pointer from CFsm::Run in the case of READ_COMPLETE, so to account for
  759. // 64-bit, we need a bigger area:
  760. LPVOID dwResultExtended = 0;
  761. DWORD &dwResult= *(DWORD *)&dwResultExtended;
  762. DWORD dwApiData= 0;
  763. if (pFsm->IsInvalid())
  764. {
  765. pFsm->SetErrorState(ERROR_WINHTTP_OPERATION_CANCELLED);
  766. pFsm->Run(lpThreadInfo, &dwResult, &dwApiData);
  767. error = ERROR_WINHTTP_OPERATION_CANCELLED;
  768. }
  769. else
  770. {
  771. error = pFsm->Run(lpThreadInfo, &dwResult, &dwApiData);
  772. }
  773. //
  774. // We should follow the following rules for this.
  775. //
  776. // 1) If Operation Failed
  777. //
  778. // error != ERROR_SUCCESS && dwResult == 0
  779. //
  780. // To assign fields of INTERNET_ASYNC_RESULT, do:
  781. //
  782. // INTERNET_ASYNC_RESULT.dwResult = 0
  783. // INTERNET_ASYNC_RESULT.dwError = error
  784. // 2) If operation Succeeded
  785. //
  786. // error == ERROR_SUCCESS && dwResult != 0
  787. //
  788. // To assign fields of INTERNET_ASYNC_RESULT, do:
  789. //
  790. // if( ApiReturnType == HINTERNET )
  791. // INTERNET_ASYNC_RESULT.dwResult = (HINTERNET)dwApiResult
  792. // else
  793. // if( ApiReturnType == BOOL )
  794. // INTERNET_ASYNC_RESULT.dwResult = TRUE
  795. // endif
  796. // endif
  797. //
  798. // INTERNET_ASYNC_RESULT.dwError = dwApiData
  799. DEBUG_PRINT(ASYNC,
  800. INFO,
  801. ("dwResult = %d [%#x], dwApiData=%d [%#x], apiType = %s, error = %d\n",
  802. dwResult, dwResult,
  803. dwApiData, dwApiData,
  804. (apiType==ApiType_Handle)?"HANDLE":"BOOL",
  805. error));
  806. if (error == ERROR_IO_PENDING) {
  807. break;
  808. }
  809. pFsm = lpThreadInfo->Fsm;
  810. if (pFsm == NULL) {
  811. if (bIsApi
  812. && ((INTERNET_HANDLE_OBJECT *)lpThreadInfo->hObjectMapped)
  813. ->IsAsyncHandle()) {
  814. INET_ASSERT((apiType == ApiType_Handle)
  815. || (apiType == ApiType_Bool));
  816. WINHTTP_ASYNC_RESULT asyncResult;
  817. /*
  818. asyncResult.dwResult = (apiType == ApiType_Handle)
  819. ? dwResult
  820. : (BOOL)(error == ERROR_SUCCESS);
  821. */
  822. asyncResult.dwResult = (error == ERROR_SUCCESS
  823. ?((apiType == ApiType_Handle)
  824. ? dwResult
  825. : TRUE)
  826. :0);
  827. //
  828. // InternetQueryDataAvailable uses dwApiData to return the
  829. // number of bytes available, in addition to returning the
  830. // the value through the lpdwNumberOfBytesAvailable parameter
  831. //
  832. asyncResult.dwError = (error == ERROR_SUCCESS)
  833. ? dwApiData
  834. : error;
  835. SetLastError(error);
  836. //INET_ASSERT(((INTERNET_HANDLE_OBJECT *)lpThreadInfo->hObjectMapped)
  837. // ->GetObjectType() == TypeHttpRequestHandle);
  838. DWORD dwStatus;
  839. DWORD dwFailureAPI = API_UNKNOWN;
  840. LPVOID lpvStatusInformation = (LPVOID)&asyncResult;
  841. DWORD dwStatusInformationLength = sizeof(asyncResult);
  842. switch(fsmType)
  843. {
  844. case FSM_TYPE_HTTP_SEND_REQUEST:
  845. if (dwResult == AR_HTTP_BEGIN_SEND_REQUEST)
  846. {
  847. dwStatus = WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE;
  848. dwFailureAPI = API_SEND_REQUEST;
  849. }
  850. else
  851. {
  852. dwStatus = WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE;
  853. dwFailureAPI = API_RECEIVE_RESPONSE;
  854. }
  855. break;
  856. case FSM_TYPE_QUERY_DATA_AVAILABLE:
  857. dwStatus = WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE;
  858. if(error == ERROR_SUCCESS)
  859. {
  860. lpvStatusInformation = NULL;
  861. dwStatusInformationLength = dwApiData;
  862. }
  863. else
  864. dwFailureAPI = API_QUERY_DATA_AVAILABLE;
  865. break;
  866. // completion for WinHttpReadData:
  867. case FSM_TYPE_READ_FILE:
  868. dwStatus = WINHTTP_CALLBACK_STATUS_READ_COMPLETE;
  869. if(error == ERROR_SUCCESS)
  870. {
  871. lpvStatusInformation = dwResultExtended;
  872. dwStatusInformationLength = dwApiData;
  873. }
  874. else
  875. dwFailureAPI = API_READ_DATA;
  876. break;
  877. case FSM_TYPE_HTTP_WRITE:
  878. dwStatus = WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE;
  879. dwFailureAPI = API_WRITE_DATA;
  880. break;
  881. default:
  882. // Shouldn't hit this, if it does, we should see if a more appropriate notification is needed:
  883. INET_ASSERT(FALSE);
  884. dwStatus = WINHTTP_CALLBACK_STATUS_REQUEST_COMPLETE;
  885. break;
  886. }
  887. if(error != ERROR_SUCCESS)
  888. {
  889. dwStatus = WINHTTP_CALLBACK_STATUS_REQUEST_ERROR;
  890. asyncResult.dwResult = dwFailureAPI;
  891. // asyncResult.dwError contains the error code as appropriate.
  892. }
  893. InternetIndicateStatus(dwStatus,
  894. lpvStatusInformation,
  895. dwStatusInformationLength
  896. );
  897. //INET_ASSERT(((INTERNET_HANDLE_OBJECT *)lpThreadInfo->hObjectMapped)
  898. // ->GetObjectType() == TypeHttpRequestHandle);
  899. DereferenceObject((LPVOID)lpThreadInfo->hObjectMapped);
  900. }
  901. break;
  902. } else if (bIsApi) {
  903. //
  904. // completing an async API that is not the last in the chain.
  905. // Typically, HttpSendRequest() within InternetOpenUrl()
  906. //
  907. DereferenceObject((LPVOID)lpThreadInfo->hObjectMapped);
  908. }
  909. }
  910. if (ThreadToken)
  911. {
  912. RevertToSelf();
  913. }
  914. PERF_LEAVE(Worker);
  915. DEBUG_LEAVE(error);
  916. return error;
  917. }
  918. DWORD
  919. CFsm::Run(
  920. IN LPINTERNET_THREAD_INFO lpThreadInfo,
  921. OUT DWORD *lpdwApiResult OPTIONAL,
  922. OUT DWORD *lpdwApiData OPTIONAL
  923. )
  924. /*++
  925. Routine Description:
  926. Runs the state handler for this FSM
  927. Arguments:
  928. lpThreadInfo - INTERNET_THREAD_INFO for this thread
  929. lpdwApiResult - where optional API result is written
  930. lpdwApiData - where optional API data iswritten
  931. Return Value:
  932. DWORD - return code from state handler
  933. --*/
  934. {
  935. DEBUG_ENTER((DBG_ASYNC,
  936. Dword,
  937. "CFsm::Run",
  938. "%#x, %#x [%#x], %#x [%#x]",
  939. lpThreadInfo,
  940. lpdwApiResult,
  941. (lpdwApiResult?*lpdwApiResult:NULL),
  942. lpdwApiData,
  943. (lpdwApiData?*lpdwApiData:NULL)
  944. ));
  945. CHECK_FSM();
  946. CHECK_OWNED();
  947. INET_ASSERT(lpThreadInfo != NULL);
  948. INET_ASSERT(lpThreadInfo->Fsm != NULL);
  949. DWORD error = ERROR_SUCCESS;
  950. _InternetSetObjectHandle(lpThreadInfo, m_hObject, m_hObjectMapped);
  951. m_lpThreadInfo = lpThreadInfo;
  952. while (TRUE) {
  953. DEBUG_PRINT(ASYNC,
  954. INFO,
  955. ("%s Fsm %#x state %s (%d) function-state %s (%d) error %s (%d)\n",
  956. MapType(),
  957. this,
  958. MapState(),
  959. GetState(),
  960. MapFunctionState(),
  961. GetFunctionState(),
  962. InternetMapError(GetError()),
  963. GetError()
  964. ));
  965. error = (*m_lpfnHandler)(this);
  966. if (error == ERROR_IO_PENDING) {
  967. break;
  968. }
  969. SetError(error);
  970. SetMappedError(lpThreadInfo->dwMappedErrorCode);
  971. if (IsDone()) {
  972. DEBUG_PRINT(ASYNC,
  973. INFO,
  974. ("%s Fsm %#x done, next is %s %#x\n",
  975. MapType(),
  976. this,
  977. m_Link ? m_Link->MapType() : "",
  978. m_Link
  979. ));
  980. if (lpdwApiResult != NULL) {
  981. *lpdwApiResult = GetApiResult();
  982. }
  983. if (lpdwApiData != NULL) {
  984. *lpdwApiData = GetApiData();
  985. }
  986. // This needs to happen only when the FSM is the top-level FSM, so we don't have to
  987. // touch the rest of the logic where CFsm::Run is called. We first save the values for
  988. // the FSMs we're interested in, in temporary variables. Then, once the FSM is destroyed
  989. // we check the LPINTERNET_THREAD_INFO to see if this was indeed the top-level-FSM.
  990. // If so, we jam the values saved into the parameters passed into Run. In this case,
  991. // we know that CFsm::RunWorkItem is the one that has called, so CFsm::RunWorkItem
  992. // knows how to interpret the values.
  993. // Stage#1:
  994. DWORD dwBytes = 0;
  995. LPVOID lpBufferExtended = 0;
  996. DWORD &lpBuffer = *(DWORD *)&lpBufferExtended;
  997. FSM_TYPE fsmType = GetType();
  998. if(fsmType == FSM_TYPE_HTTP_SEND_REQUEST)
  999. lpBuffer = (((CFsm_HttpSendRequest *)this)->m_arRequest);
  1000. if(error == ERROR_SUCCESS)
  1001. {
  1002. switch(fsmType)
  1003. {
  1004. case FSM_TYPE_QUERY_DATA_AVAILABLE:
  1005. dwBytes = *(((CFsm_QueryAvailable *)this)->m_lpdwNumberOfBytesAvailable);
  1006. break;
  1007. // completion for WinHttpReadData:
  1008. case FSM_TYPE_READ_FILE:
  1009. lpBufferExtended = (LPVOID) (((CFsm_ReadFile *)this)->m_lpBuffer);
  1010. dwBytes = *(((CFsm_ReadFile *)this)->m_lpdwNumberOfBytesRead);
  1011. break;
  1012. }
  1013. }
  1014. // End Stage#1
  1015. DEBUG_PRINT(ASYNC,
  1016. INFO,
  1017. ("Fsm %#x finished with lpdwApiResult = %#x[%#x], lpdwApiData = %#x[%#x]\n",
  1018. this,
  1019. lpdwApiResult,
  1020. (lpdwApiResult == NULL)?NULL:*lpdwApiResult,
  1021. lpdwApiData,
  1022. (lpdwApiData == NULL)?NULL:*lpdwApiData
  1023. ));
  1024. INET_ASSERT (!IsOnAsyncList());
  1025. delete this;
  1026. // Stage#2:
  1027. if (lpThreadInfo->Fsm == NULL) {
  1028. // top-level completion:
  1029. if(fsmType == FSM_TYPE_HTTP_SEND_REQUEST)
  1030. if (lpdwApiResult != NULL)
  1031. *lpdwApiResult = lpBuffer;
  1032. if(error == ERROR_SUCCESS)
  1033. {
  1034. switch(fsmType)
  1035. {
  1036. case FSM_TYPE_QUERY_DATA_AVAILABLE:
  1037. if (lpdwApiData != NULL)
  1038. *lpdwApiData = dwBytes;
  1039. break;
  1040. // completion for WinHttpReadData:
  1041. case FSM_TYPE_READ_FILE:
  1042. if (lpdwApiResult != NULL)
  1043. {
  1044. LPVOID &lpApiResultExtended = *(LPVOID *)lpdwApiResult;
  1045. lpApiResultExtended = lpBufferExtended;
  1046. }
  1047. if (lpdwApiData != NULL)
  1048. *lpdwApiData = dwBytes;
  1049. break;
  1050. }
  1051. }
  1052. }
  1053. // End Stage#2
  1054. break;
  1055. }
  1056. SetState(GetNextState());
  1057. }
  1058. DEBUG_LEAVE(error);
  1059. return error;
  1060. }
  1061. #if INET_DEBUG
  1062. #if !defined(CASE_OF)
  1063. #define CASE_OF(x) case x: return #x
  1064. #endif
  1065. DEBUG_FUNCTION
  1066. LPSTR
  1067. CFsm::MapType(
  1068. VOID
  1069. ) {
  1070. switch (m_Type) {
  1071. case FSM_TYPE_NONE: return "NONE";
  1072. case FSM_TYPE_WAIT_FOR_COMPLETION: return "WAIT_FOR_COMPLETION";
  1073. case FSM_TYPE_RESOLVE_HOST: return "RESOLVE_HOST";
  1074. case FSM_TYPE_SOCKET_CONNECT: return "SOCKET_CONNECT";
  1075. case FSM_TYPE_SOCKET_SEND: return "SOCKET_SEND";
  1076. case FSM_TYPE_SOCKET_RECEIVE: return "SOCKET_RECEIVE";
  1077. case FSM_TYPE_SOCKET_QUERY_AVAILABLE: return "SOCKET_QUERY_AVAILABLE";
  1078. case FSM_TYPE_SECURE_CONNECT: return "SECURE_CONNECT";
  1079. case FSM_TYPE_SECURE_HANDSHAKE: return "SECURE_HANDSHAKE";
  1080. case FSM_TYPE_SECURE_NEGOTIATE: return "SECURE_NEGOTIATE";
  1081. case FSM_TYPE_NEGOTIATE_LOOP: return "NEGOTIATE_LOOP";
  1082. case FSM_TYPE_SECURE_SEND: return "SECURE_SEND";
  1083. case FSM_TYPE_SECURE_RECEIVE: return "SECURE_RECEIVE";
  1084. case FSM_TYPE_GET_CONNECTION: return "GET_CONNECTION";
  1085. case FSM_TYPE_HTTP_SEND_REQUEST: return "HTTP_SEND_REQUEST";
  1086. case FSM_TYPE_MAKE_CONNECTION: return "MAKE_CONNECTION";
  1087. case FSM_TYPE_OPEN_CONNECTION: return "OPEN_CONNECTION";
  1088. case FSM_TYPE_OPEN_PROXY_TUNNEL: return "OPEN_PROXY_TUNNEL";
  1089. case FSM_TYPE_SEND_REQUEST: return "SEND_REQUEST";
  1090. case FSM_TYPE_RECEIVE_RESPONSE: return "RECEIVE_RESPONSE";
  1091. case FSM_TYPE_HTTP_READ: return "HTTP_READ";
  1092. case FSM_TYPE_HTTP_WRITE: return "HTTP_WRITE";
  1093. case FSM_TYPE_READ_DATA: return "READ_DATA";
  1094. case FSM_TYPE_HTTP_QUERY_AVAILABLE: return "HTTP_QUERY_AVAILABLE";
  1095. case FSM_TYPE_DRAIN_RESPONSE: return "DRAIN_RESPONSE";
  1096. case FSM_TYPE_REDIRECT: return "REDIRECT";
  1097. case FSM_TYPE_READ_LOOP: return "READ_LOOP";
  1098. case FSM_TYPE_PARSE_HTTP_URL: return "PARSE_HTTP_URL";
  1099. case FSM_TYPE_PARSE_URL_FOR_HTTP: return "PARSE_URL_FOR_HTTP";
  1100. case FSM_TYPE_READ_FILE: return "READ_FILE";
  1101. case FSM_TYPE_READ_FILE_EX: return "READ_FILE_EX";
  1102. case FSM_TYPE_WRITE_FILE: return "WRITE_FILE";
  1103. case FSM_TYPE_QUERY_DATA_AVAILABLE: return "QUERY_DATA_AVAILABLE";
  1104. case FSM_TYPE_FTP_CONNECT: return "FTP_CONNECT";
  1105. case FSM_TYPE_FTP_FIND_FIRST_FILE: return "FTP_FIND_FIRST_FILE";
  1106. case FSM_TYPE_FTP_GET_FILE: return "FTP_GET_FILE";
  1107. case FSM_TYPE_FTP_PUT_FILE: return "FTP_PUT_FILE";
  1108. case FSM_TYPE_FTP_DELETE_FILE: return "FTP_DELETE_FILE";
  1109. case FSM_TYPE_FTP_RENAME_FILE: return "FTP_RENAME_FILE";
  1110. case FSM_TYPE_FTP_OPEN_FILE: return "FTP_OPEN_FILE";
  1111. case FSM_TYPE_FTP_CREATE_DIRECTORY: return "FTP_CREATE_DIRECTORY";
  1112. case FSM_TYPE_FTP_REMOVE_DIRECTORY: return "FTP_REMOVE_DIRECTORY";
  1113. case FSM_TYPE_FTP_SET_CURRENT_DIRECTORY: return "FTP_SET_CURRENT_DIRECTORY";
  1114. case FSM_TYPE_FTP_GET_CURRENT_DIRECTORY: return "FTP_GET_CURRENT_DIRECTORY";
  1115. case FSM_TYPE_GOPHER_FIND_FIRST_FILE: return "GOPHER_FIND_FIRST_FILE";
  1116. case FSM_TYPE_GOPHER_OPEN_FILE: return "GOPHER_OPEN_FILE";
  1117. case FSM_TYPE_GOPHER_GET_ATTRIBUTE: return "GOPHER_GET_ATTRIBUTE";
  1118. case FSM_TYPE_INTERNET_PARSE_URL: return "INTERNET_PARSE_URL";
  1119. case FSM_TYPE_INTERNET_FIND_NEXT_FILE: return "INTERNET_FIND_NEXT_FILE";
  1120. case FSM_TYPE_INTERNET_QUERY_DATA_AVAILABLE: return "INTERNET_QUERY_DATA_AVAILABLE";
  1121. case FSM_TYPE_INTERNET_WRITE_FILE: return "INTERNET_WRITE_FILE";
  1122. case FSM_TYPE_INTERNET_READ_FILE: return "INTERNET_READ_FILE";
  1123. }
  1124. return "?";
  1125. }
  1126. DEBUG_FUNCTION
  1127. LPSTR
  1128. CFsm::StateName(
  1129. IN DWORD State
  1130. ) {
  1131. switch (State) {
  1132. CASE_OF(FSM_STATE_BAD);
  1133. CASE_OF(FSM_STATE_INIT);
  1134. CASE_OF(FSM_STATE_WAIT);
  1135. CASE_OF(FSM_STATE_DONE);
  1136. CASE_OF(FSM_STATE_ERROR);
  1137. CASE_OF(FSM_STATE_CONTINUE);
  1138. CASE_OF(FSM_STATE_FINISH);
  1139. CASE_OF(FSM_STATE_1);
  1140. CASE_OF(FSM_STATE_2);
  1141. CASE_OF(FSM_STATE_3);
  1142. CASE_OF(FSM_STATE_4);
  1143. CASE_OF(FSM_STATE_5);
  1144. CASE_OF(FSM_STATE_6);
  1145. CASE_OF(FSM_STATE_7);
  1146. CASE_OF(FSM_STATE_8);
  1147. CASE_OF(FSM_STATE_9);
  1148. CASE_OF(FSM_STATE_10);
  1149. }
  1150. return "?";
  1151. }
  1152. #endif // INET_DEBUG