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.

772 lines
20 KiB

  1. /*++
  2. Copyright (c) 1995 Microsoft Corporation
  3. Module Name :
  4. asyncio.cxx
  5. Abstract:
  6. This module implements functions for ASYNC_IO_CONNECTION Object.
  7. Author:
  8. Murali R. Krishnan ( MuraliK ) 27-March-1995
  9. Environment:
  10. User Mode -- Win32
  11. Project:
  12. Internet Services DLL
  13. Revision History:
  14. --*/
  15. /************************************************************
  16. * Include Headers
  17. ************************************************************/
  18. // need to include ftpdp.hxx here since precompiled header used.
  19. # include "ftpdp.hxx"
  20. # include "dbgutil.h"
  21. # include "asyncio.hxx"
  22. # include "..\..\infocomm\atq\atqtypes.hxx"
  23. /************************************************************
  24. * Functions
  25. ************************************************************/
  26. ASYNC_IO_CONNECTION::ASYNC_IO_CONNECTION(
  27. IN PFN_ASYNC_IO_COMPLETION pfnAioCompletion,
  28. IN SOCKET sClient OPTIONAL
  29. )
  30. : m_pAioContext ( NULL),
  31. m_pfnAioCompletion ( pfnAioCompletion),
  32. m_sClient ( sClient),
  33. m_fFileIO ( FALSE),
  34. m_sTimeout ( DEFAULT_CONNECTION_IO_TIMEOUT),
  35. m_pAtqContext ( NULL),
  36. m_endpointObject ( NULL)
  37. {
  38. IF_DEBUG( ASYNC_IO) {
  39. DBGPRINTF( ( DBG_CONTEXT,
  40. " Created a new ASYNC_IO_CONNECTION object ( %08x)\n",
  41. this
  42. ));
  43. }
  44. } // ASYNC_IO_CONNECTION::ASYNC_IO_CONNECTION()
  45. ASYNC_IO_CONNECTION::~ASYNC_IO_CONNECTION( VOID)
  46. /*++
  47. This function cleans up the ASYNC_IO_CONNECTION object. It also frees
  48. up sockets and ATQ context embedded in this object.
  49. THIS IS NOT MULTI_THREAD safe!
  50. --*/
  51. {
  52. IF_DEBUG( ASYNC_IO) {
  53. DBGPRINTF( ( DBG_CONTEXT,
  54. "Deleting the ASYNC_IO_CONNECTION object ( %08x) \n",
  55. this
  56. ));
  57. }
  58. if ( (!m_fFileIO && m_sClient != INVALID_SOCKET) ||
  59. ( m_fFileIO && m_sClient != (SOCKET)INVALID_HANDLE_VALUE) ) {
  60. //
  61. // Shut and Close the socket. This can fail, if the socket is already
  62. // closed by some other thread before this operation completes
  63. //
  64. StopIo( NO_ERROR);
  65. }
  66. if ( m_pAtqContext != NULL) {
  67. AtqFreeContext( m_pAtqContext, TRUE );
  68. m_pAtqContext = NULL;
  69. }
  70. DBG_ASSERT( m_sClient == INVALID_SOCKET);
  71. } // ASYNC_IO_CONNECTION::~ASYN_IO_CONNECTION()
  72. BOOL
  73. ASYNC_IO_CONNECTION::ReadFile(
  74. OUT LPVOID pvBuffer,
  75. IN DWORD cbSize,
  76. IN LONGLONG liOffset
  77. )
  78. /*++
  79. This starts off an Asynchronous read operation for data from client
  80. into the supplied buffer.
  81. Arguments:
  82. pvBuffer pointer to byte buffer which on successful
  83. return will contain the data read from client
  84. cbSize count of bytes of data available in the buffer.
  85. ( limits the size of data that can be read)
  86. liOffset When writing to file, use this offset to initialize the ATQ
  87. overlapped structure
  88. Returns:
  89. TRUE on success and FALSE if there is a failure in setting up read.
  90. --*/
  91. {
  92. BOOL fReturn = TRUE;
  93. DBG_ASSERT( pvBuffer != NULL);
  94. IF_DEBUG( ASYNC_IO) {
  95. DBGPRINTF( ( DBG_CONTEXT,
  96. " Entering ASYNC_IO_CONNECTION( %08x)::"
  97. "ReadFile( %08x, %u)\n",
  98. this, pvBuffer, cbSize
  99. ));
  100. }
  101. //
  102. // if no ATQ context yet, create one.
  103. // set offset in ATQ overlapped structure
  104. // initiate the async read
  105. //
  106. if ( (m_pAtqContext == NULL && !AddToAtqHandles()) ||
  107. (m_fFileIO && AtqContextSetInfo(
  108. QueryAtqContext(),
  109. ATQ_INFO_SET_OVL_OFFSET,
  110. (ULONG_PTR)&liOffset)) ||
  111. !AtqReadFile( m_pAtqContext, pvBuffer, cbSize, NULL )) {
  112. IF_DEBUG( ASYNC_IO) {
  113. DWORD dwError = GetLastError();
  114. DBGPRINTF(( DBG_CONTEXT,
  115. "ASYNC_IO_CONNECTION(%08x)::ReadFile() failed."
  116. " Error = %u\n",
  117. this, dwError));
  118. SetLastError( dwError);
  119. }
  120. fReturn = FALSE;
  121. }
  122. return ( fReturn);
  123. } // ASYNC_IO_CONNECTION::ReadFile()
  124. BOOL
  125. ASYNC_IO_CONNECTION::WriteFile(
  126. OUT LPVOID pvBuffer,
  127. IN DWORD cbSize,
  128. IN LONGLONG liOffset
  129. )
  130. /*++
  131. This starts off an Asynchronous write operation to send data to the client
  132. sending data from the supplied buffer. The buffer may not be freed
  133. until the data is sent out.
  134. Arguments:
  135. pvBuffer pointer to byte buffer which contains the data to be sent
  136. to the client.
  137. cbSize count of bytes of data to be sent.
  138. liOffset When writing to file, use this offset to initialize the ATQ
  139. overlapped structure
  140. Returns:
  141. TRUE on success and FALSE if there is a failure in setting up write.
  142. --*/
  143. {
  144. BOOL fReturn = TRUE;
  145. DBG_ASSERT( pvBuffer != NULL);
  146. IF_DEBUG( ASYNC_IO) {
  147. DBGPRINTF( ( DBG_CONTEXT,
  148. " Entering ASYNC_IO_CONNECTION( %08x)::"
  149. "WriteFile( %08x, %u)\n",
  150. this, pvBuffer, cbSize
  151. ));
  152. }
  153. //
  154. // Check and add to create an atq context as well as perform
  155. // the write operation.
  156. //
  157. // set offset in ATQ overlapped structure
  158. //
  159. if ( (m_pAtqContext == NULL && !AddToAtqHandles()) ||
  160. (m_fFileIO && AtqContextSetInfo(
  161. QueryAtqContext(),
  162. ATQ_INFO_SET_OVL_OFFSET,
  163. (ULONG_PTR)&liOffset)) ||
  164. !AtqWriteFile( m_pAtqContext, pvBuffer, cbSize, NULL )) {
  165. IF_DEBUG( ASYNC_IO) {
  166. DWORD dwError = GetLastError();
  167. DBGPRINTF(( DBG_CONTEXT,
  168. "ASYNC_IO_CONNECTION(%08x)::WriteFile() failed."
  169. " Error = %u\n",
  170. this, dwError));
  171. SetLastError( dwError);
  172. }
  173. fReturn = FALSE;
  174. }
  175. return ( fReturn);
  176. } // ASYNC_IO_CONNECTION::WriteFile()
  177. BOOL
  178. ASYNC_IO_CONNECTION::TransmitFile(
  179. IN HANDLE hFile,
  180. IN LARGE_INTEGER & liSize,
  181. IN LONGLONG llOffset,
  182. IN LPTRANSMIT_FILE_BUFFERS lpTransmitBuffers OPTIONAL
  183. )
  184. /*++
  185. This starts off an Asynchronous TransmitFile operation to send file data
  186. to the client.
  187. Arguments:
  188. hFile handle for the file to be transmitted.
  189. liSize large integer containing size of file to be sent.
  190. Offset Offset within the file to begin transmitting.
  191. lpTransmitBuffers pointer to File Transmit Buffers
  192. Returns:
  193. TRUE on success and FALSE if there is a failure in setting up read.
  194. --*/
  195. {
  196. BOOL fReturn = TRUE;
  197. DBG_ASSERT( hFile != INVALID_HANDLE_VALUE);
  198. IF_DEBUG( ASYNC_IO) {
  199. DBGPRINTF( ( DBG_CONTEXT,
  200. " Entering ASYNC_IO_CONNECTION( %08x)::"
  201. "TransmitFile( %08x, %l, %ul, %08x)\n",
  202. this, hFile, liSize.HighPart, liSize.LowPart,
  203. lpTransmitBuffers
  204. ));
  205. }
  206. if ( m_pAtqContext == NULL )
  207. {
  208. if (!AddToAtqHandles())
  209. {
  210. IF_DEBUG( ASYNC_IO)
  211. {
  212. DWORD dwError = GetLastError();
  213. DBGPRINTF(( DBG_CONTEXT,
  214. "ASYNC_IO_CONNECTION(%08x)::AddToAtqHandles() failed."
  215. " Error = %u\n",
  216. this, dwError));
  217. SetLastError( dwError);
  218. }
  219. return FALSE;
  220. }
  221. }
  222. AtqContextSetInfo( QueryAtqContext(),
  223. ATQ_INFO_SET_OVL_OFFSET,
  224. (ULONG_PTR)&llOffset);
  225. if (!AtqTransmitFile( m_pAtqContext,
  226. hFile,
  227. ((liSize.HighPart == 0) ? liSize.LowPart : 0),
  228. lpTransmitBuffers,
  229. TF_DISCONNECT )
  230. )
  231. {
  232. IF_DEBUG( ASYNC_IO) {
  233. DWORD dwError = GetLastError();
  234. DBGPRINTF(( DBG_CONTEXT,
  235. "ASYNC_IO_CONNECTION(%08x)::TransmitFile() failed."
  236. " Error = %u\n",
  237. this, dwError));
  238. SetLastError( dwError);
  239. }
  240. fReturn = FALSE;
  241. }
  242. return ( fReturn);
  243. } // ASYNC_IO_CONNECTION::TransmitFile()
  244. BOOL
  245. ASYNC_IO_CONNECTION::TransmitFileTs(
  246. IN TS_OPEN_FILE_INFO * pOpenFile,
  247. IN LARGE_INTEGER & liSize,
  248. IN LONGLONG llOffset,
  249. IN BOOL fDisconnectSocket
  250. )
  251. /*++
  252. This starts off an Asynchronous TransmitFile operation to send file data
  253. to the client.
  254. Arguments:
  255. hFile handle for the file to be transmitted.
  256. liSize large integer containing size of file to be sent.
  257. Offset Offset within the file to begin transmitting.
  258. fDisconnectSocket is the socket getting disconnected after this transmit,
  259. or this is a part of a large file, so keep socket open.
  260. Returns:
  261. TRUE on success and FALSE if there is a failure in setting up read.
  262. --*/
  263. {
  264. BOOL fReturn = TRUE;
  265. PBYTE pFileBuffer = NULL;
  266. HANDLE hFile = NULL;
  267. TRANSMIT_FILE_BUFFERS TransmitBuffers;
  268. DBG_ASSERT( pOpenFile );
  269. IF_DEBUG( ASYNC_IO) {
  270. DBGPRINTF( ( DBG_CONTEXT,
  271. " Entering ASYNC_IO_CONNECTION( %08x)::"
  272. "TransmitFile( %p, %p, %ul, %08x)\n",
  273. this, pOpenFile, liSize.HighPart, liSize.LowPart
  274. ));
  275. }
  276. if ( m_pAtqContext == NULL )
  277. {
  278. if (!AddToAtqHandles())
  279. {
  280. IF_DEBUG( ASYNC_IO)
  281. {
  282. DWORD dwError = GetLastError();
  283. DBGPRINTF(( DBG_CONTEXT,
  284. "ASYNC_IO_CONNECTION(%08x)::AddToAtqHandles() failed."
  285. " Error = %u\n",
  286. this, dwError));
  287. SetLastError( dwError);
  288. }
  289. return FALSE;
  290. }
  291. }
  292. pFileBuffer = pOpenFile->QueryFileBuffer();
  293. if (pFileBuffer) {
  294. //
  295. // Transmit from memory
  296. //
  297. DBG_ASSERT( liSize.HighPart == 0 );
  298. TransmitBuffers.Head = pFileBuffer + (DWORD)llOffset;
  299. TransmitBuffers.HeadLength = liSize.LowPart;
  300. TransmitBuffers.Tail = NULL;
  301. TransmitBuffers.TailLength = 0;
  302. } else {
  303. //
  304. // Transmit from a file
  305. //
  306. hFile = pOpenFile->QueryFileHandle();
  307. AtqContextSetInfo( QueryAtqContext(),
  308. ATQ_INFO_SET_OVL_OFFSET,
  309. (ULONG_PTR)&llOffset);
  310. if (liSize.HighPart != 0)
  311. {
  312. LARGE_INTEGER liTimeOut;
  313. ULONG Remainder;
  314. liTimeOut =
  315. RtlExtendedLargeIntegerDivide(
  316. liSize,
  317. (ULONG) 1024,
  318. &Remainder);
  319. if (liTimeOut.HighPart != 0)
  320. {
  321. ((PATQ_CONT) m_pAtqContext)->TimeOut = ATQ_INFINITE;
  322. } else
  323. {
  324. ((PATQ_CONT) m_pAtqContext)->TimeOut = liTimeOut.LowPart;
  325. }
  326. }
  327. }
  328. if (!AtqTransmitFile( m_pAtqContext,
  329. hFile,
  330. ((liSize.HighPart == 0) ? liSize.LowPart : 0),
  331. pFileBuffer ? &TransmitBuffers : NULL,
  332. fDisconnectSocket ? TF_DISCONNECT : 0)
  333. )
  334. {
  335. IF_DEBUG( ASYNC_IO) {
  336. DWORD dwError = GetLastError();
  337. DBGPRINTF(( DBG_CONTEXT,
  338. "ASYNC_IO_CONNECTION(%08x)::TransmitFile() failed."
  339. " Error = %u\n",
  340. this, dwError));
  341. SetLastError( dwError);
  342. }
  343. fReturn = FALSE;
  344. }
  345. return ( fReturn);
  346. } // ASYNC_IO_CONNECTION::TransmitFile()
  347. BOOL
  348. ASYNC_IO_CONNECTION::StopIo( IN DWORD dwErrorCode OPTIONAL)
  349. /*++
  350. This function stops the io connection by performing a hard close on
  351. the socket that is used for IO. that is the only way one can easily kill the
  352. IO that is in progress.
  353. Arguments:
  354. dwErrorCode DWORD containing the error code for stopping IO
  355. Returns:
  356. TRUE on success and FALSE if there is a failure.
  357. --*/
  358. {
  359. INT serr = 0;
  360. SOCKET sOld = m_sClient;
  361. m_sClient = INVALID_SOCKET;
  362. IF_DEBUG( ASYNC_IO) {
  363. DBGPRINTF( ( DBG_CONTEXT,
  364. " ASYNC_IO_CONNECTION( %08x)::StopIo( %u)\n",
  365. this, dwErrorCode
  366. ));
  367. }
  368. //
  369. // nothing to do if the m_sClient not valid
  370. //
  371. if ( !m_fFileIO && (sOld == INVALID_SOCKET) ) {
  372. goto StopIo_exit;
  373. }
  374. //
  375. // NYI! dwErrorCode is not at present used.
  376. //
  377. if ( m_fFileIO ) {
  378. HANDLE hHandle = (HANDLE)sOld;
  379. m_fFileIO = FALSE;
  380. DBG_ASSERT( hHandle != INVALID_HANDLE_VALUE );
  381. if (m_pAtqContext != NULL) {
  382. if (!AtqCloseFileHandle( m_pAtqContext)) {
  383. serr = GetLastError();
  384. }
  385. } else {
  386. if ( !CloseHandle( hHandle)) {
  387. serr = GetLastError();
  388. }
  389. }
  390. } else {
  391. if (m_pAtqContext != NULL) {
  392. //
  393. // per the FTP RFC, the server must close the socket when killing a data
  394. // channel.
  395. //
  396. if (!AtqCloseSocket( m_pAtqContext, TRUE)) {
  397. serr = GetLastError();
  398. }
  399. } else {
  400. // Ignore failures in Shutdown and close socket.
  401. if (closesocket( sOld) == SOCKET_ERROR) {
  402. serr = WSAGetLastError();
  403. }
  404. }
  405. if ( serr != 0 ) {
  406. SetLastError( serr);
  407. }
  408. }
  409. StopIo_exit:
  410. return ( serr == 0);
  411. } // ASYNC_IO_CONNECTION::StopIo()
  412. BOOL
  413. ASYNC_IO_CONNECTION::SetNewSocket(IN SOCKET sNewSocket,
  414. IN PATQ_CONTEXT pNewAtqContext, // = NULL
  415. IN PVOID EndpointObject )
  416. /*++
  417. This function changes the socket maintained for given ASYNC_IO_CONNECTION
  418. object. It changes it only if the current socket in the object is already
  419. freed (by calling StopIo()).
  420. If the Atq Context in this object is a valid one corresponding to old
  421. socket, it is also freed. So any new operation will create a new AtqContext.
  422. (This is essential, since there is a one-to-one-relationship between socket
  423. and ATQ context)
  424. Arguments:
  425. sNewSocket new socket for the connection
  426. If sNewSocket == INVALID_SOCKET then this function does
  427. cleanup of old information.
  428. pNewAtqContext new ATQ Context for the socket
  429. Returns:
  430. TRUE on success and FALSE if there is any failure.
  431. --*/
  432. {
  433. BOOL fReturn = TRUE;
  434. DBG_ASSERT( !m_fFileIO);
  435. if (m_sClient == INVALID_SOCKET) {
  436. //
  437. // Free the Atq Context if one already exists.
  438. // ==> Reason: There is a one-to-one correspondence b/w ATQ Context
  439. // and socket. The atq context if valid was created
  440. // for old connection.
  441. //
  442. // To avoid race conditions, we exchange pointer with NULL
  443. // and later on free the object as need be.
  444. // Should we necessarily use InterlockedExchange() ???
  445. // Isn't it costly? NYI
  446. PATQ_CONTEXT pAtqContext =
  447. (PATQ_CONTEXT ) InterlockedExchangePointer( (PVOID *) &m_pAtqContext,
  448. (PVOID) pNewAtqContext);
  449. if ( pAtqContext != NULL) {
  450. AtqFreeContext( pAtqContext, TRUE );
  451. }
  452. m_sClient = sNewSocket;
  453. m_endpointObject = EndpointObject;
  454. } else {
  455. SetLastError( ERROR_INVALID_PARAMETER);
  456. fReturn = FALSE;
  457. }
  458. return ( fReturn);
  459. } // ASYNC_IO_CONNECTION::SetNewSocket()
  460. BOOL
  461. ASYNC_IO_CONNECTION::SetNewFile(IN HANDLE hNewFile)
  462. /*++
  463. Similar to SetNewSocket above.
  464. Arguments:
  465. hNewFile new file handle
  466. If hNewFile == INVALID_HANDLE_VALUE then this function does
  467. cleanup of old information.
  468. pNewAtqContext new ATQ Context for the socket
  469. Returns:
  470. TRUE on success and FALSE if there is any failure.
  471. --*/
  472. {
  473. BOOL fReturn;
  474. fReturn = SetNewSocket( NULL);
  475. if ( fReturn) {
  476. m_fFileIO = TRUE;
  477. m_sClient = (SOCKET)hNewFile;
  478. }
  479. return fReturn;
  480. } // ASYNC_IO_CONNECTION::SetNewFile()
  481. # if DBG
  482. VOID ASYNC_IO_CONNECTION::Print( VOID) const
  483. {
  484. DBGPRINTF( ( DBG_CONTEXT,
  485. " Printing ASYNC_IO_CONNECTION( %08x)\n"
  486. " CallBackFunction = %08x; Context = %08x\n"
  487. " Client Socket = %u; AtqContext = %08x;"
  488. " Timeout = %u sec; \n",
  489. this, m_pfnAioCompletion, m_pAioContext,
  490. m_sClient, m_pAtqContext, m_sTimeout));
  491. return;
  492. } // ASYNC_IO_CONNECTION::Print()
  493. # endif // DBG
  494. VOID
  495. ProcessAtqCompletion(IN LPVOID pContext,
  496. IN DWORD cbIo,
  497. IN DWORD dwCompletionStatus,
  498. IN OVERLAPPED * lpo
  499. )
  500. /*++
  501. This function processes the completion of an atq operation.
  502. It also sends a call back to the owner of this object ( ASYNC_IO_CONNECTION)
  503. object, once the operation is completed or if it is in error.
  504. ATQ module sends 2 messages whenever there is a timeout.
  505. Reason: The timeout itself is sent by a separate thread and completion port
  506. API does not support removal of a socket from the completion port. Combining
  507. these together, ATQ sends a separate timeout message and then when the
  508. application blows off the socket/handle, ATQ sends another error message
  509. implying failure of the connection.
  510. We handle this as follows:
  511. At timeout ATQ sends fIOCompletion == FALSE and
  512. dwCompletionStatus == ERROR_SEM_TIMEOUT.
  513. We send this as fTimedOut in call back.
  514. The application can check if it is fTimedOut and hence refrain from
  515. blowing the object completely away.
  516. Arguments:
  517. pContext pointer to User supplied context information
  518. ( here: pointer to ASYNC_IO_CONNECTION associated with
  519. the IO completed)
  520. cbIo count of bytes of IO performed
  521. dwCompletionStatus DWORD containing error code if any
  522. lpo - !NULL if completion from IO
  523. Returns:
  524. None
  525. --*/
  526. {
  527. LPASYNC_IO_CONNECTION pConn = (LPASYNC_IO_CONNECTION ) pContext;
  528. BOOL fTimedOut = FALSE;
  529. if ( pConn == NULL) {
  530. // This should not happen....
  531. SetLastError( ERROR_INVALID_PARAMETER);
  532. DBG_ASSERT( pConn == NULL);
  533. return ;
  534. }
  535. IF_DEBUG( ASYNC_IO) {
  536. DBGPRINTF( ( DBG_CONTEXT,
  537. "ProcessAtqCompletion(Aio=%08x, cb=%u, Status=%u,"
  538. "IO Compltion=%s).\n",
  539. pConn, cbIo, dwCompletionStatus,
  540. lpo != NULL ? "TRUE" : "FALSE" ));
  541. }
  542. if ( lpo != NULL ||
  543. (fTimedOut = (
  544. lpo == NULL &&
  545. dwCompletionStatus == ERROR_SEM_TIMEOUT))
  546. ) {
  547. //
  548. // This is the right Atq object. Process the response by passing it
  549. // to the owner of this object.
  550. //
  551. DBG_ASSERT( pConn->QueryPfnAioCompletion() != NULL);
  552. //
  553. // Invoke the call back function for completion of IO.
  554. //
  555. ( *pConn->QueryPfnAioCompletion())
  556. (pConn->QueryAioContext(), cbIo, dwCompletionStatus,
  557. pConn, fTimedOut);
  558. }
  559. return;
  560. } // ProcessAtqCompletion()
  561. /************************ End of File ***********************/