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

5335 lines
139 KiB

  1. /**********************************************************************/
  2. /** Microsoft Windows NT **/
  3. /** Copyright(c) Microsoft Corp., 1993 **/
  4. /**********************************************************************/
  5. /*
  6. userdb.cxx
  7. This module manages the user database for the FTPD Service.
  8. Functions exported by this module:
  9. DisconnectUser()
  10. DisconnectUsersWithNoAccess()
  11. EnumerateUsers()
  12. USER_DATA::USER_DATA()
  13. USER_DATA::Reset()
  14. USER_DATA::~USER_DATA()
  15. USER_DATA::Cleanup()
  16. USER_DATA::ProcessAsyncIoCompletion()
  17. USER_DATA::ReInitializeForNewUser()
  18. USER_DATA::ReadCommand()
  19. USER_DATA::DisconnectUserWithError()
  20. USER_DATA::SendMultilineMessage()
  21. USER_DATA::SendDirectoryAnnotation()
  22. USER_DATA::GetFileSize();
  23. ProcessUserAsyncIoCompletion()
  24. FILE HISTORY:
  25. KeithMo 07-Mar-1993 Created.
  26. MuraliK March-May, 1995
  27. Adding support for Async Io/Transfers
  28. + new USER_DATA class functions defined.
  29. + oob_inline enabled; ReadCommand() issued after
  30. data socket is established.
  31. + added member functions for common operations
  32. + added ProcessAsyncIoCompletion()
  33. + added Establish & Destroy of Data connection
  34. MuraliK 26-July-1995 Added Allocation caching of client conns.
  35. Terryk 18-Sep-1996 Added GetFileSize
  36. AMallet Sep 1998 Added support for AcceptEx() of PASV data connections
  37. */
  38. #include "ftpdp.hxx"
  39. # include "tsunami.hxx"
  40. #include <timer.h>
  41. # include "auxctrs.h"
  42. # include <mbstring.h>
  43. #include "acptctxt.hxx"
  44. #define FIRST_TELNET_COMMAND 240
  45. #define TELNET_DM_COMMAND 242
  46. #define TELNET_IP_COMMAND 244
  47. #define TELNET_SB_CODE 250
  48. #define TELNET_SB_CODE_MIN 251
  49. #define TELNET_SB_CODE_MAX 254
  50. #define TELNET_IAC_CODE 255
  51. # define MAX_FILE_SIZE_SPEC ( 32)
  52. //
  53. // Private globals.
  54. //
  55. #define PSZ_DEFAULT_SUB_DIRECTORY "Default"
  56. static const char PSZ_SENT_VERB[] = "sent";
  57. static const char PSZ_CONNECTION_CLOSED_VERB[] = "closed";
  58. static const char PSZ_FILE_ERROR[] = "%s: %s";
  59. static const char PSZ_TRANSFER_COMPLETE[] = "Transfer complete.";
  60. static const char PSZ_TRANSFER_ABORTED[] =
  61. "Connection closed; transfer aborted.";
  62. static const char PSZ_TRANSFER_STARTING[] =
  63. "Data connection already open; Transfer starting.";
  64. static const char PSZ_INSUFFICIENT_RESOURCES[] =
  65. "Insufficient system resources.";
  66. static const char PSZ_TOO_MANY_PASV_USERS[] =
  67. "Too many passive-mode users.";
  68. static const char PSZ_OPENING_DATA_CONNECTION[] =
  69. "Opening %s mode data connection for %s%s.";
  70. static const char PSZ_CANNOT_OPEN_DATA_CONNECTION[] =
  71. "Can't open data connection.";
  72. static const char PSZ_COMMAND_TOO_LONG[] =
  73. "Command was too long";
  74. static DWORD p_NextUserId = 0; // Next available user id.
  75. static const char SZ_LOCALUSER_DIR[] = "LocalUser\\";
  76. static const char SZ_ANONYMOUS_DIR[] = "Public";
  77. //
  78. // Private prototypes.
  79. //
  80. DWORD
  81. UserpGetNextId(
  82. VOID
  83. );
  84. inline VOID
  85. StopControlRead( IN LPUSER_DATA pUserData)
  86. /*++
  87. Stops control read operation, if one is proceeding.
  88. Resets the CONTROL_READ flag as well as decrements ref count in user data.
  89. --*/
  90. {
  91. if ( TEST_UF( pUserData, CONTROL_READ)) {
  92. if ( InterlockedDecrement( &pUserData->m_nControlRead) < 0 ) {
  93. DBGPRINTF(( DBG_CONTEXT,
  94. "StopControLRead: no read active!!!\n"));
  95. DBG_ASSERT( FALSE);
  96. }
  97. DBG_REQUIRE( pUserData->DeReference() > 0);
  98. CLEAR_UF( pUserData, CONTROL_READ);
  99. }
  100. } // StopControlRead()
  101. BOOL
  102. FilterTelnetCommands(IN CHAR * pszLine, IN DWORD cchLine,
  103. IN LPBOOL pfLineEnded,
  104. IN LPDWORD pcchRequestRecvd)
  105. /*++
  106. Filters out the Telnet commands and
  107. terminates the command line with linefeed.
  108. Also this function filters out the out of band data.
  109. This works similar to the Sockutil.cxx::DiscardOutOfBandData().
  110. We scan for the pattern "ABOR\r\n" and
  111. set the OOB_DATA flag if it is present.
  112. Arguments:
  113. pszLine pointer to null terminated string containing the input data.
  114. cchLine count of characters of data received
  115. pfLineEnded pointer to Boolean flag which is set to true if complete
  116. line has been received.
  117. pcchRequestRecvd pointer to DWORD which on return contains the number
  118. of bytes received.
  119. Returns:
  120. TRUE if the filtering is successful without any out of band abort request.
  121. FALSE if there was any abort request in the input.
  122. --*/
  123. {
  124. BOOL fDontAbort = TRUE;
  125. BOOL fStateTelnetCmd = FALSE;
  126. BOOL fStateTelnetSB = FALSE;
  127. BOOL fFoundTelnetIP = FALSE;
  128. CHAR * pszSrc;
  129. CHAR * pszDst;
  130. LPCSTR pszAbort = "ABOR\r\n";
  131. LPCSTR pszNext = pszAbort;
  132. DBG_ASSERT( pszLine != NULL && cchLine > 0 &&
  133. pfLineEnded != NULL && pcchRequestRecvd != NULL);
  134. *pfLineEnded = FALSE;
  135. for( pszSrc = pszDst = pszLine; pszSrc < pszLine + cchLine && *pszSrc;
  136. pszSrc++) {
  137. CHAR ch = *pszSrc;
  138. BYTE uch = (BYTE)ch;
  139. //
  140. // Filter out TELNET commands. these are of the form: IAC <cmd> or
  141. // IAC SB <op> (IAC = 255, SB = 250, op 251..254, cmd > 240)
  142. //
  143. if( fStateTelnetCmd ) {
  144. //
  145. // we are in a Telbent command sequence
  146. //
  147. fStateTelnetCmd = FALSE;
  148. DBG_ASSERT( uch >= FIRST_TELNET_COMMAND );
  149. if( fStateTelnetSB ) {
  150. //
  151. // we are in a Telnet subsequence command
  152. //
  153. fStateTelnetSB = FALSE;
  154. DBG_ASSERT( (uch >= TELNET_SB_CODE_MIN) &&
  155. (uch <= TELNET_SB_CODE_MAX) );
  156. if( uch >= FIRST_TELNET_COMMAND ) {
  157. //
  158. // consider it a valid Telnet command, as long as it's in
  159. // the Telnet range. Filter this char out.
  160. //
  161. continue;
  162. }
  163. //
  164. // this is a TELNET protocol error, we'll ignore it.
  165. //
  166. // fall through with this char
  167. //
  168. } else if( uch == TELNET_SB_CODE ) {
  169. //
  170. // enter Telnet subsequense command state
  171. //
  172. fStateTelnetCmd = fStateTelnetSB = TRUE;
  173. continue;
  174. } else if( uch == TELNET_IAC_CODE ) {
  175. //
  176. // this is an escape sequence for a 255 data byte
  177. //
  178. // let it fall through
  179. //
  180. } else if ( uch == TELNET_IP_COMMAND ) {
  181. //
  182. // remember this, it is the first in a SYNCH sequence
  183. //
  184. fFoundTelnetIP = TRUE;
  185. continue;
  186. } else if ( uch == TELNET_DM_COMMAND ) {
  187. //
  188. // if in a SYNCH sequence, this resets the input stream
  189. //
  190. if( fFoundTelnetIP ) {
  191. pszDst = pszLine;
  192. fFoundTelnetIP = FALSE; // completed the SYNCH sequence
  193. }
  194. continue;
  195. } else {
  196. //
  197. // we expect a Telnet command code here. filter it out
  198. //
  199. DBG_ASSERT( uch >= FIRST_TELNET_COMMAND );
  200. if ( uch >= FIRST_TELNET_COMMAND ) {
  201. continue;
  202. }
  203. //
  204. // this is a TELNET protocol error, we'll ignore it.
  205. //
  206. // fall through with this char
  207. //
  208. }
  209. } else if( uch == TELNET_IAC_CODE ) {
  210. //
  211. // entering Telnet command parsing state
  212. //
  213. fStateTelnetCmd = TRUE;
  214. continue;
  215. } else if( uch == TELNET_DM_COMMAND ) {
  216. //
  217. // FTP.EXE on Win2k is sending an unexpected SYNCH sequence: DM, IAC, IP. See if this is it.
  218. //
  219. if( ( pszSrc == pszLine ) &&
  220. ( cchLine >= 3 ) &&
  221. ( (UINT)*(pszSrc+1) == TELNET_IAC_CODE ) &&
  222. ( (UINT)*(pszSrc+2) == TELNET_DM_COMMAND ) ) {
  223. //
  224. // just filter the sequence out
  225. //
  226. pszSrc += 2;
  227. continue;
  228. } else if( fFoundTelnetIP ) {
  229. //
  230. // or, it could be a single byte URGENT notification in the telnet Sync
  231. //
  232. pszDst = pszLine;
  233. fFoundTelnetIP = FALSE; // completed the SYNCH sequence
  234. continue;
  235. }
  236. }
  237. //
  238. // if we have seen a Telnet IP, then skip everything up to a DM
  239. //
  240. if (fFoundTelnetIP) {
  241. continue;
  242. }
  243. //
  244. // try matching ABOR\r\n
  245. //
  246. if ( *pszNext != ch) {
  247. // the pattern match failed. reset to start at the beginning.
  248. pszNext = pszAbort;
  249. }
  250. if ( *pszNext == ch) {
  251. // pattern match at this character. move forward
  252. pszNext++;
  253. if ( *pszNext == '\0') { // end of string==> all matched.
  254. // only consider this an OOB Abort if at the beginning of
  255. // a (reset) line
  256. if( (pszDst - pszLine + 2) == (pszNext - pszAbort) ) {
  257. fDontAbort = FALSE;
  258. }
  259. pszNext = pszAbort;
  260. }
  261. }
  262. //
  263. // don't copy <CR> and <LF> to the output
  264. //
  265. if ( (ch != '\r') && ( ch != '\n')) {
  266. *pszDst++ = ch;
  267. } else if ( ch == '\n') {
  268. // terminate at the linefeed
  269. *pfLineEnded = TRUE;
  270. break;
  271. }
  272. } // for
  273. //
  274. // remember Telnet IP if we have seen it
  275. //
  276. if (fFoundTelnetIP) {
  277. //
  278. // we can safely do this, as we have filtered the 2 byte sequence out earlier
  279. //
  280. *(UCHAR*)pszDst++ = TELNET_IAC_CODE;
  281. *(UCHAR*)pszDst++ = TELNET_IP_COMMAND;
  282. }
  283. //
  284. // remember Telnet command states
  285. //
  286. if( fStateTelnetCmd ) {
  287. *(UCHAR*)pszDst++ = TELNET_IAC_CODE;
  288. if( fStateTelnetSB ) {
  289. *(UCHAR*)pszDst++ = TELNET_SB_CODE;
  290. }
  291. }
  292. *pszDst = '\0';
  293. *pcchRequestRecvd = DIFF(pszDst - pszLine);
  294. DBG_ASSERT( *pcchRequestRecvd <= cchLine);
  295. return (fDontAbort);
  296. } // FilterTelnetCommands()
  297. //
  298. // Public functions.
  299. //
  300. USER_DATA::USER_DATA(
  301. IN FTP_SERVER_INSTANCE *pInstance
  302. )
  303. /*++
  304. This function creates a new UserData object for the information
  305. required to process requests from a new User connection ( FTP).
  306. Arguments:
  307. sControl Socket used for control channel in FTP connection
  308. clientIpAddress strcuture containing the client Ip address
  309. Returns:
  310. a newly constructed USER_DATA object.
  311. Check IsValid() to ensure the object was properly created.
  312. NOTE:
  313. This function is to be used for dummy creation of the object so
  314. allocation cacher can use this object.
  315. Fields are randomly initialized. Reset() will initialize them properly.
  316. However when a new effective USER_DATA object is needed, after allocation
  317. one can call USER_DATA::Reset() to initialize all vars.
  318. --*/
  319. :
  320. m_References ( 0),
  321. m_ActiveRefAdded ( 0),
  322. m_cchRecvBuffer ( 0),
  323. m_cbRecvd ( 0),
  324. m_cchPartialReqRecvd ( 0),
  325. m_pOpenFileInfo ( NULL),
  326. Flags ( 0),
  327. UserToken ( NULL),
  328. m_UserId ( 0),
  329. DataPort ( 0),
  330. UserState ( UserStateEmbryonic),
  331. m_AioControlConnection ( ProcessUserAsyncIoCompletion),
  332. m_AioDataConnection ( ProcessUserAsyncIoCompletion),
  333. m_sPassiveDataListen ( INVALID_SOCKET),
  334. CurrentDirHandle ( INVALID_HANDLE_VALUE),
  335. RenameSourceBuffer ( NULL),
  336. m_fCleanedup ( FALSE),
  337. m_pMetaData ( NULL),
  338. m_pInstance ( pInstance ),
  339. m_acCheck ( AC_NOT_CHECKED ),
  340. m_fNeedDnsCheck ( FALSE ),
  341. m_dwLastReplyCode ( 0 ),
  342. m_fHavePASVConn ( FALSE ),
  343. m_fWaitingForPASVConn ( FALSE ),
  344. m_fFakeIOCompletion ( FALSE ),
  345. m_pszCmd ( NULL ),
  346. m_hPASVAcceptEvent ( NULL )
  347. #if DBG
  348. ,m_RefTraceLog( NULL )
  349. #endif
  350. {
  351. DWORD dwTimeout = m_pInstance->QueryConnectionTimeout();
  352. INITIALIZE_CRITICAL_SECTION( &m_UserLock );
  353. //
  354. // Setup the structure signature.
  355. //
  356. INIT_USER_SIG( this );
  357. m_AioControlConnection.SetAioInformation( this, dwTimeout);
  358. m_AioDataConnection.SetAioInformation( this, dwTimeout);
  359. InitializeListHead( &ListEntry);
  360. ZeroMemory( m_recvBuffer, DEFAULT_REQUEST_BUFFER_SIZE);
  361. IF_DEBUG( USER_DATABASE ) {
  362. DBGPRINTF(( DBG_CONTEXT,
  363. "user_data object created @ %08lX.\n",
  364. this));
  365. }
  366. m_licbSent.QuadPart = 0;
  367. #if DBG
  368. m_RefTraceLog = CreateRefTraceLog( TRACE_LOG_SIZE, 0 );
  369. #endif
  370. FakeIOTimes = 0;
  371. } // USER_DATA::USER_DATA()
  372. USER_DATA::~USER_DATA(VOID)
  373. {
  374. if ( !m_fCleanedup) {
  375. Cleanup();
  376. }
  377. if( RenameSourceBuffer != NULL ) {
  378. TCP_FREE( RenameSourceBuffer);
  379. RenameSourceBuffer = NULL;
  380. }
  381. if ( m_pszCmd != NULL )
  382. {
  383. TCP_FREE( m_pszCmd );
  384. m_pszCmd = NULL;
  385. }
  386. if ( m_hPASVAcceptEvent != NULL )
  387. {
  388. RemovePASVAcceptEvent( TRUE );
  389. }
  390. if ( m_pInstance != NULL ) {
  391. m_pInstance->DecrementCurrentConnections();
  392. m_pInstance->Dereference();
  393. m_pInstance = NULL;
  394. }
  395. #if DBG
  396. if( m_RefTraceLog != NULL ) {
  397. DestroyRefTraceLog( m_RefTraceLog );
  398. }
  399. #endif
  400. DeleteCriticalSection( &m_UserLock );
  401. } // USER_DATA::~USER_DATA()
  402. BOOL
  403. USER_DATA::Reset(IN SOCKET sControl,
  404. IN PVOID EndpointObject,
  405. IN IN_ADDR clientIpAddress,
  406. IN const SOCKADDR_IN * psockAddrLocal /* = NULL */ ,
  407. IN PATQ_CONTEXT pAtqContext /* = NULL */ ,
  408. IN PVOID pvInitialRequest /* = NULL */ ,
  409. IN DWORD cbWritten /* = 0 */ ,
  410. IN AC_RESULT acCheck
  411. )
  412. {
  413. BOOL fReturn = TRUE;
  414. //
  415. // Setup the structure signature.
  416. //
  417. INIT_USER_SIG( this );
  418. m_References = 1; // set to 1 to prevent immediate deletion.
  419. m_ActiveRefAdded= 1;
  420. m_fCleanedup = FALSE;
  421. Flags = m_pInstance->QueryUserFlags();
  422. UserState = UserStateEmbryonic;
  423. #if DBG
  424. if( m_RefTraceLog != NULL ) {
  425. ResetTraceLog( m_RefTraceLog );
  426. }
  427. #endif
  428. m_pOpenFileInfo = NULL;
  429. UserToken = NULL;
  430. if ( m_pMetaData != NULL )
  431. {
  432. TsFreeMetaData( m_pMetaData->QueryCacheInfo() );
  433. m_pMetaData = NULL;
  434. }
  435. m_UserId = UserpGetNextId();
  436. m_xferType = XferTypeAscii;
  437. m_xferMode = XferModeStream;
  438. m_msStartingTime= 0;
  439. m_acCheck = acCheck;
  440. m_fNeedDnsCheck = FALSE;
  441. m_dwLastReplyCode = 0;
  442. HostIpAddress = clientIpAddress;
  443. DataIpAddress = clientIpAddress;
  444. m_cbRecvd = 0;
  445. m_cchRecvBuffer = sizeof( m_recvBuffer) - sizeof(m_recvBuffer[0]);
  446. m_cchPartialReqRecvd = 0;
  447. CurrentDirHandle = INVALID_HANDLE_VALUE;
  448. RenameSourceBuffer = NULL;
  449. m_TimeAtConnection = GetCurrentTimeInSeconds();
  450. m_TimeAtLastAccess = m_TimeAtConnection;
  451. m_pvInitialRequest = pvInitialRequest;
  452. m_cbInitialRequest = cbWritten;
  453. //
  454. // clean up the stuff needed to deal async with PASV command
  455. //
  456. if ( m_pszCmd )
  457. {
  458. TCP_FREE( m_pszCmd );
  459. m_pszCmd = NULL;
  460. }
  461. if ( m_hPASVAcceptEvent )
  462. {
  463. RemovePASVAcceptEvent( TRUE );
  464. }
  465. CleanupPASVFlags();
  466. // set up the async io contexts
  467. m_AioControlConnection.SetNewSocket( sControl, pAtqContext, EndpointObject );
  468. m_AioDataConnection.SetNewSocket(INVALID_SOCKET);
  469. m_sPassiveDataListen = ( INVALID_SOCKET);
  470. m_rgchFile[0] = '\0';
  471. m_szUserName[0] = '\0'; // no user name available yet.
  472. m_szCurrentDirectory[0] = '\0'; // initialize to no virtual dir.
  473. m_szRootDir[0] = '\0'; // no root directory known yet
  474. m_licbSent.QuadPart = 0;
  475. m_pInstance->QueryStatsObj()->IncrCurrentConnections();
  476. m_dwCurrentOffset = 0;
  477. m_dwNextOffset = 0;
  478. //
  479. // get the local Ip address
  480. //
  481. if ( psockAddrLocal != NULL) {
  482. LocalIpAddress = psockAddrLocal->sin_addr;
  483. LocalIpPort = psockAddrLocal->sin_port;
  484. } else {
  485. SOCKADDR_IN saddrLocal;
  486. INT cbLocal;
  487. cbLocal = sizeof( saddrLocal);
  488. if ( getsockname( sControl, (SOCKADDR *) &saddrLocal, &cbLocal) != 0) {
  489. DWORD err = WSAGetLastError();
  490. fReturn = FALSE;
  491. IF_DEBUG( ERROR) {
  492. DBGPRINTF( ( DBG_CONTEXT,
  493. " Failure in getsockname( sock=%d). Error = %u\n",
  494. sControl, err));
  495. }
  496. SetLastError( err);
  497. } else {
  498. LocalIpAddress = saddrLocal.sin_addr;
  499. LocalIpPort = saddrLocal.sin_port;
  500. }
  501. }
  502. DataPort = CONN_PORT_TO_DATA_PORT(LocalIpPort);
  503. //
  504. // Success!
  505. //
  506. IF_DEBUG( CLIENT) {
  507. time_t now;
  508. time( & now);
  509. CHAR pchAddr[32];
  510. InetNtoa( clientIpAddress, pchAddr);
  511. DBGPRINTF( ( DBG_CONTEXT,
  512. " Client Connection for %s:%d starting @ %s",
  513. pchAddr, sControl,
  514. asctime( localtime( &now))));
  515. }
  516. IF_DEBUG( USER_DATABASE ) {
  517. DBGPRINTF(( DBG_CONTEXT,
  518. "user %lu reset @ %08lX.\n",
  519. QueryId(), this));
  520. }
  521. m_nControlRead = 0;
  522. FakeIOTimes = 0;
  523. return (fReturn);
  524. } // USER_DATA::Reset()
  525. VOID
  526. USER_DATA::Cleanup( VOID)
  527. /*++
  528. This cleans up data stored in the user data object.
  529. Returns:
  530. None
  531. --*/
  532. {
  533. DBG_ASSERT( QueryReference() == 0);
  534. if ( m_pMetaData != NULL )
  535. {
  536. TsFreeMetaData( m_pMetaData->QueryCacheInfo() );
  537. m_pMetaData = NULL;
  538. }
  539. # if DBG
  540. if ( !IS_VALID_USER_DATA( this)) {
  541. DBGPRINTF( ( DBG_CONTEXT,
  542. "Encountering an invalid user data ( %08x)\n",
  543. this));
  544. Print();
  545. }
  546. # endif // DBG
  547. DBG_ASSERT( IS_VALID_USER_DATA( this ) );
  548. IF_DEBUG( USER_DATABASE ) {
  549. DBGPRINTF(( DBG_CONTEXT,
  550. " Cleaning up user %lu @ %08lX.\n",
  551. QueryId(), this));
  552. }
  553. DBG_ASSERT( m_nControlRead == 0);
  554. //
  555. // Clean up stuff needed to deal with PASV connections
  556. //
  557. if ( m_hPASVAcceptEvent )
  558. {
  559. RemovePASVAcceptEvent( TRUE );
  560. }
  561. if ( m_pszCmd )
  562. {
  563. TCP_FREE( m_pszCmd );
  564. m_pszCmd = NULL;
  565. }
  566. //
  567. // Close any open sockets & handles.
  568. //
  569. CloseSockets( FALSE );
  570. // invalidate the connections
  571. m_AioControlConnection.SetNewSocket(INVALID_SOCKET);
  572. m_AioDataConnection.SetNewSocket(INVALID_SOCKET);
  573. //
  574. // Update the statistics.
  575. //
  576. if( IsLoggedOn()
  577. && !TEST_UF( this, WAIT_PASS ) )
  578. {
  579. if( TEST_UF( this, ANONYMOUS))
  580. {
  581. m_pInstance->QueryStatsObj()->DecrCurrentAnonymousUsers();
  582. }
  583. else
  584. {
  585. m_pInstance->QueryStatsObj()->DecrCurrentNonAnonymousUsers();
  586. }
  587. }
  588. m_pInstance->QueryStatsObj()->DecrCurrentConnections();
  589. if( UserToken != NULL )
  590. {
  591. TsDeleteUserToken( UserToken );
  592. UserToken = NULL;
  593. }
  594. if( CurrentDirHandle != INVALID_HANDLE_VALUE )
  595. {
  596. IF_DEBUG( VIRTUAL_IO )
  597. {
  598. DBGPRINTF(( DBG_CONTEXT,
  599. "closing directory handle %08lX\n",
  600. CurrentDirHandle ));
  601. }
  602. CloseHandle( CurrentDirHandle );
  603. CurrentDirHandle = INVALID_HANDLE_VALUE;
  604. }
  605. if ( m_pOpenFileInfo != NULL) {
  606. DBG_REQUIRE( CloseFileForSend());
  607. }
  608. //
  609. // Release the memory attached to this structure.
  610. //
  611. if( RenameSourceBuffer != NULL ) {
  612. // do not free this location until end of usage.
  613. RenameSourceBuffer[0] = '\0';
  614. }
  615. m_UserId = 0; // invalid User Id
  616. //
  617. // Kill the structure signature.
  618. //
  619. KILL_USER_SIG( this );
  620. IF_DEBUG( CLIENT) {
  621. time_t now;
  622. time( & now);
  623. DBGPRINTF( ( DBG_CONTEXT,
  624. " Client Connection for %s:%d ending @ %s",
  625. inet_ntoa( HostIpAddress), QueryControlSocket(),
  626. asctime( localtime( &now))));
  627. }
  628. //
  629. // There is a possible race condition. If the socket was abruptly closed
  630. // and there was any pending Io, they will get blown away. This will
  631. // cause a call-back from the ATQ layer. That is unavoidable.
  632. // In such cases it is possible that the object was deleted.
  633. // This can lead to problems. We need to be careful.
  634. // But Reference Count protects against such disasters. So tread
  635. // carefully and use Reference count.
  636. //
  637. DBG_ASSERT( m_sPassiveDataListen == INVALID_SOCKET);
  638. m_fCleanedup = TRUE; // since we just cleaned up this object
  639. return;
  640. } // USER_DATA::Cleanup()
  641. VOID
  642. USER_DATA::ReInitializeForNewUser( VOID)
  643. /*++
  644. This function reinitializes the user data information for a new user to
  645. communicate with the server using existing control socket connection.
  646. --*/
  647. {
  648. # if DBG
  649. if ( !IS_VALID_USER_DATA( this)) {
  650. DBGPRINTF( ( DBG_CONTEXT,
  651. "Encountering an invalid user data ( %08x)\n",
  652. this));
  653. Print();
  654. }
  655. # endif // DBG
  656. DBG_ASSERT( IS_VALID_USER_DATA( this ) );
  657. //
  658. // Update the statistics.
  659. //
  660. if( IsLoggedOn())
  661. {
  662. if( TEST_UF( this, ANONYMOUS))
  663. {
  664. m_pInstance->QueryStatsObj()->DecrCurrentAnonymousUsers();
  665. }
  666. else
  667. {
  668. m_pInstance->QueryStatsObj()->DecrCurrentNonAnonymousUsers();
  669. }
  670. }
  671. CLEAR_UF_BITS( this, (UF_LOGGED_ON | UF_ANONYMOUS | UF_PASSIVE));
  672. LockUser();
  673. if( QueryState() != UserStateDisconnected ) {
  674. SetState( UserStateWaitingForUser );
  675. }
  676. UnlockUser();
  677. if ( m_pMetaData != NULL )
  678. {
  679. TsFreeMetaData( m_pMetaData->QueryCacheInfo() );
  680. m_pMetaData = NULL;
  681. }
  682. m_TimeAtConnection= GetCurrentTimeInSeconds();
  683. m_TimeAtLastAccess= m_TimeAtConnection;
  684. m_xferType = XferTypeAscii;
  685. m_xferMode = XferModeStream;
  686. DataIpAddress = HostIpAddress;
  687. DataPort = CONN_PORT_TO_DATA_PORT(LocalIpPort);
  688. m_szUserName[0] = '\0';
  689. m_szCurrentDirectory[0] = '\0';
  690. m_szRootDir[0] = '\0';
  691. if( UserToken != NULL )
  692. {
  693. TsDeleteUserToken( UserToken );
  694. UserToken = NULL;
  695. }
  696. if( CurrentDirHandle != INVALID_HANDLE_VALUE )
  697. {
  698. IF_DEBUG( VIRTUAL_IO )
  699. {
  700. DBGPRINTF(( DBG_CONTEXT,
  701. "closing directory handle %08lX\n",
  702. CurrentDirHandle ));
  703. }
  704. CloseHandle( CurrentDirHandle );
  705. CurrentDirHandle = INVALID_HANDLE_VALUE;
  706. }
  707. if ( m_pOpenFileInfo != NULL) {
  708. DBG_REQUIRE( CloseFileForSend());
  709. }
  710. m_licbSent.QuadPart = 0;
  711. m_pvInitialRequest = NULL;
  712. m_cbInitialRequest = 0;
  713. CleanupPassiveSocket( TRUE );
  714. return;
  715. } // USER_DATA::ReInitializeForNewUser()
  716. BOOL
  717. USER_DATA::ProcessAsyncIoCompletion(
  718. IN DWORD cbIo,
  719. IN DWORD dwError,
  720. IN LPASYNC_IO_CONNECTION pAioConn,
  721. IN BOOL fTimedOut)
  722. /*++
  723. This function processes the Async Io completion.
  724. ( invoked due to a callback from the ASYNC_IO_CONNECTION object)
  725. Arguments:
  726. pContext pointer to the context information ( UserData object).
  727. cbIo count of bytes transferred in Io
  728. dwError DWORD containing the error code resulting from last tfr.
  729. pAioConn pointer to AsyncIo connection object.
  730. fTimedOut flag indicating if the current call was made
  731. because of timeout.
  732. Returns:
  733. None
  734. --*/
  735. {
  736. BOOL fReturn = FALSE;
  737. AC_RESULT acDnsAccess;
  738. DWORD dwOriginalError;
  739. dwOriginalError = dwError;
  740. //
  741. // Special processing if it's an IO completion on the control connection - we might
  742. // be processing a completion we posted ourselves to signal that the data socket for the PASV
  743. // data connection is now accept()'able.
  744. //
  745. if ( pAioConn == &m_AioControlConnection && QueryInFakeIOCompletion() )
  746. {
  747. // Here is a horrible race condition:
  748. // If the FTP client closes the control socket
  749. // right after having finished receiving the transmitted file
  750. // than there may be a thread that enters this
  751. // code path (because the FakeIO flag is set, and the
  752. // Control Socket is involved) before the IO completion
  753. // for the data sonnection has traveled this same function,
  754. // cleaning the FakeIO flag
  755. // Here is the race condition:
  756. // A thread enter here, and see that the FakeIO is set
  757. // the normal behavior is reprocessing a command like
  758. // "RETR foo.txt", while now the command if a zero length string.
  759. // the Second thread enter this function with the DataConnection
  760. // it clears the flag (at a non specified point of the
  761. // processing of the other thread) and it exit.
  762. // the original thread is now processing a saved string
  763. // (because of the FakeIO flag) while it is not supposed to.
  764. // this causes problems to the time-out algorithm, because
  765. // of a ref-count problem in the USER_DATA
  766. LONG CurVal = InterlockedIncrement(&(this->FakeIOTimes));
  767. if (CurVal>1){
  768. goto NormalProcessing;
  769. }
  770. //
  771. // Remove the reference used to deal with the race condition between an IO
  772. // thread doing clean-up and the thread watching for the data socket to become
  773. // accept()'able and holding on to this USER_DATA object
  774. //
  775. DeReference();
  776. //
  777. // There is a race condition between the thread watching for a socket to become
  778. // accept()'able and an IO thread being woken up because the client has (unexpectedly)
  779. // disconnected. The thread watching the socket will post a fake IO completion to
  780. // indicate that an accept() on the socket will succeed; however, if the client
  781. // disconnects (the control connection) before the fake completion is processed,
  782. // we don't want to do any more processing.
  783. //
  784. if ( UserState == UserStateDisconnected )
  785. {
  786. return TRUE;
  787. }
  788. else
  789. {
  790. //
  791. // Fast-path if we know this is the second time around we're trying to process the
  792. // command, which happens when we're in PASV mode
  793. //
  794. DBG_ASSERT( UserState == UserStateLoggedOn );
  795. goto ProcessCommand;
  796. }
  797. }
  798. NormalProcessing:
  799. if( dwError != NO_ERROR &&
  800. dwError != ERROR_SEM_TIMEOUT )
  801. {
  802. //
  803. // Geezsh, I hate my life.
  804. //
  805. // Once upon a time, there was a bug in ATQ that cause it to
  806. // always pass NO_ERROR as the status to the async completion
  807. // routine. This bug caused, among other things, FTP to never
  808. // time out idle connections, because it never saw the
  809. // ERROR_SEM_TIMEOUT status. So, I fixed the bug in ATQ.
  810. //
  811. // Now, this completion routine gets the actual status. Well,
  812. // that breaks service shutdown when there are connected users.
  813. // Basically, when a shutdown occurs, the connected sockets are
  814. // closed, causing the IO to complete with ERROR_NETNAME_DELETED.
  815. // USER_DATA::ProcessAsyncIoCompletion() is not handling this
  816. // error properly, which causes 1) an assertion failure because
  817. // USER_DATA::DisconnectUserWithError() is getting called *twice*
  818. // and 2) the service never stops because of a dangling reference
  819. // on the USER_DATA structure.
  820. //
  821. // Of course, the proper thing to do would be to fix the offending
  822. // code in USER_DATA::ProcessAsyncIoCompletion() so that it DID
  823. // handle the error properly. Unfortunately, that fix requires a
  824. // nontrivial amount of surgery, and we're a scant three days
  825. // from releasing K2 Beta 1. So...
  826. //
  827. // As a quick & dirty work around for K2 Beta 1, we'll map all
  828. // errors other than ERROR_SEM_TIMEOUT to NO_ERROR. This should
  829. // provide the lower software layers with the old ATQ behavior
  830. // they're expecting.
  831. //
  832. // REMOVE THIS POST BETA 1 AND FIX USER_DATA PROPERLY!!!!
  833. //
  834. // 3/12/98
  835. //
  836. // N.B. The debug output below has been changed to be a little
  837. // more customer friendly but I hate to prevent future developers
  838. // for enjoying the original message which read:
  839. // "Mapping error %d to NO_ERROR to mask FTP bug (FIX!)\n"
  840. //
  841. // I'm removing this message because it was the source of some
  842. // embarrasment, when a checked version of this DLL was sent to
  843. // Ernst & Young to track the now famous bug #138566.
  844. //
  845. DBGPRINTF((
  846. DBG_CONTEXT,
  847. "Mapping error %d to NO_ERROR\n",
  848. dwError
  849. ));
  850. dwError = NO_ERROR;
  851. }
  852. # if DBG
  853. if ( !IS_VALID_USER_DATA( this)) {
  854. DBGPRINTF( ( DBG_CONTEXT,
  855. "Encountering an invalid user data ( %08x)\n",
  856. this));
  857. Print();
  858. }
  859. # endif // DBG
  860. DBG_ASSERT( IS_VALID_USER_DATA( this ) );
  861. IF_DEBUG( USER_DATABASE) {
  862. DBGPRINTF( ( DBG_CONTEXT,
  863. "[%lu] Entering USER_DATA( %08x)::Process( %u, %u, %08x)."
  864. " RefCount = %d. State = %d\n",
  865. GetTickCount(),
  866. this, cbIo, dwError, pAioConn, QueryReference(),
  867. QueryState()));
  868. }
  869. if ( m_fNeedDnsCheck )
  870. {
  871. acDnsAccess = QueryAccessCheck()->CheckDnsAccess();
  872. UnbindInstanceAccessCheck();
  873. m_fNeedDnsCheck = FALSE;
  874. if ( (acDnsAccess == AC_IN_DENY_LIST) ||
  875. (acDnsAccess == AC_NOT_IN_GRANT_LIST) ||
  876. ((m_acCheck == AC_NOT_IN_GRANT_LIST) &&
  877. (acDnsAccess != AC_IN_GRANT_LIST) ) ) {
  878. ReplyToUser(this,
  879. REPLY_NOT_LOGGED_IN,
  880. "Connection refused, unknown IP address." );
  881. DisconnectUserWithError( NO_ERROR );
  882. return TRUE;
  883. }
  884. }
  885. if ( pAioConn == &m_AioDataConnection)
  886. {
  887. //
  888. // a Data transfer operation has completed.
  889. //
  890. DBG_REQUIRE( IsLoggedOn());
  891. // Update last access time
  892. m_TimeAtLastAccess = GetCurrentTimeInSeconds();
  893. if ( dwError == NO_ERROR || !fTimedOut)
  894. {
  895. // dwError == NO_ERROR ==> No error in transmitting data
  896. // so decrease ref count and blow away the sockets.
  897. // if dwError != NO_ERROR then
  898. // if timeout occured ==> ATQ will send another callback
  899. // so do not decrease ref count now.
  900. // if no timeout ==> then decrement ref count now.
  901. DBG_REQUIRE( DeReference() > 0);
  902. }
  903. else
  904. {
  905. if ( fTimedOut)
  906. {
  907. SET_UF( this, DATA_TIMEDOUT);
  908. }
  909. else
  910. {
  911. SET_UF( this, DATA_ERROR);
  912. }
  913. }
  914. # ifdef CHECK_DBG
  915. if ( dwError != NO_ERROR)
  916. {
  917. CHAR szBuffer[100];
  918. sprintf( szBuffer, " Data Socket Error = %u ", dwError);
  919. Print( szBuffer);
  920. }
  921. # endif // CHECK_DBG
  922. CLEAR_UF( this, ASYNC_TRANSFER);
  923. //
  924. // Destroy the data connection.
  925. // Send message accordingly to indicate if this was a failure/success
  926. // That is done by DestroyDataConnection.
  927. //
  928. DBG_REQUIRE( DestroyDataConnection( dwOriginalError));
  929. if ( m_pOpenFileInfo != NULL)
  930. {
  931. //
  932. // set number of bytes actually sent
  933. //
  934. m_licbSent.QuadPart += cbIo;
  935. DBG_REQUIRE( CloseFileForSend( dwOriginalError));
  936. }
  937. if ( dwError == NO_ERROR)
  938. {
  939. //
  940. // Process any Pending commands, due to the parallel
  941. // control channel operation for this user Connection.
  942. // For the present, we dont buffer commands ==> No processing
  943. // to be done effectively. NYI
  944. // Just ensure that there is a read-operation pending on
  945. // control channel.
  946. //
  947. // BOGUS: DBG_ASSERT( TEST_UF( this, CONTROL_READ));
  948. }
  949. fReturn = TRUE; // since this function went on well.
  950. }
  951. else if ( pAioConn == &m_AioControlConnection)
  952. {
  953. //
  954. // a control socket operation has completed.
  955. //
  956. if ( dwError != NO_ERROR)
  957. {
  958. //
  959. // There is an error in processing the control connection request.
  960. // the only ASYNC_IO request we submit on control is:
  961. // Read request on control socket
  962. //
  963. if ( fTimedOut)
  964. {
  965. if ( TEST_UF( this, TRANSFER))
  966. {
  967. // A data transfer is going on.
  968. // allow client to send commands later
  969. // (client may not be async in control/data io,so allow it)
  970. // resubmit the control read operation
  971. // after clearing old one
  972. //
  973. // Since there is a pending IO in atq.
  974. // Just resume the timeout processing in ATQ for
  975. // this context.
  976. //
  977. pAioConn->ResumeIoOperation();
  978. fReturn = TRUE;
  979. }
  980. else
  981. {
  982. // For timeouts, ATQ sends two call backs.
  983. // So be careful to decrement reference count only once.
  984. DBG_ASSERT( fReturn == FALSE);
  985. DBG_ASSERT( TEST_UF( this, CONTROL_READ));
  986. SET_UF( this, CONTROL_TIMEDOUT);
  987. }
  988. }
  989. else
  990. {
  991. // Either there should be a control read pending or
  992. // control socket should have received a timeout.
  993. DBG_ASSERT( TEST_UF( this, CONTROL_READ) ||
  994. TEST_UF( this, CONTROL_TIMEDOUT)
  995. );
  996. // a non-timeout error has occured. ==> stop read operation.
  997. StopControlRead(this);
  998. DBG_ASSERT( fReturn == FALSE);
  999. SET_UF( this, CONTROL_ERROR);
  1000. }
  1001. }
  1002. else
  1003. {
  1004. // If this connection had an outstanding IO on wait queue, it
  1005. // got completed. Hence get rid of the reference count.
  1006. StopControlRead( this);
  1007. switch ( UserState)
  1008. {
  1009. case UserStateEmbryonic:
  1010. fReturn = StartupSession( m_pvInitialRequest,
  1011. m_cbInitialRequest);
  1012. if ( m_pvInitialRequest == NULL)
  1013. {
  1014. // No initial buffer. Wait for read to complete
  1015. break;
  1016. }
  1017. cbIo = m_cbInitialRequest; // fake the bytes read.
  1018. // Fall Through for processing request
  1019. case UserStateWaitingForUser:
  1020. case UserStateWaitingForPass:
  1021. case UserStateLoggedOn:
  1022. ProcessCommand:
  1023. //
  1024. // Input already read. Process request and submit another read.
  1025. //
  1026. fReturn = ParseAndProcessRequest(cbIo/sizeof(CHAR));
  1027. if ( fReturn && IsDisconnected() &&
  1028. TEST_UF( this, CONTROL_TIMEDOUT))
  1029. {
  1030. // disconnect only if no pending control read
  1031. // if there is a pending control read,
  1032. // atq will pop this up for cleanup.
  1033. fReturn = !(TEST_UF( this, CONTROL_READ));
  1034. IF_DEBUG( ERROR) {
  1035. DBGPRINTF(( DBG_CONTEXT,
  1036. "%08x ::Timeout killed conn while "
  1037. " processing!\n State = %d(%x),"
  1038. " Ref = %d, Id = %d, fRet=%d\n",
  1039. this, QueryState(), Flags,
  1040. QueryReference(), QueryId(), fReturn
  1041. ));
  1042. }
  1043. FacIncrement( CacTimeoutWhenProcessing);
  1044. }
  1045. break;
  1046. case UserStateDisconnected:
  1047. fReturn = TRUE;
  1048. if ( TEST_UF( this, CONTROL_TIMEDOUT))
  1049. {
  1050. // Timeout thread raced against me :(
  1051. IF_DEBUG( ERROR) {
  1052. DBGPRINTF(( DBG_CONTEXT,
  1053. "%08x :: Conn already Disconnected !!!\n"
  1054. " State = %d(%x), Ref = %d, Id = %d\n",
  1055. this, QueryState(), Flags,
  1056. QueryReference(), QueryId()
  1057. ));
  1058. }
  1059. FacIncrement( CacTimeoutInDisconnect);
  1060. fReturn = FALSE;
  1061. }
  1062. break;
  1063. default:
  1064. DBG_ASSERT( !"Invalid UserState for processing\n");
  1065. SetLastError( ERROR_INVALID_PARAMETER);
  1066. break;
  1067. } // switch
  1068. dwError = ( fReturn) ? NO_ERROR : GetLastError();
  1069. }
  1070. if ( !fReturn)
  1071. {
  1072. DisconnectUserWithError( dwError, fTimedOut);
  1073. }
  1074. }
  1075. else
  1076. {
  1077. DBG_ASSERT( !"call to Process() with wrong parameters");
  1078. }
  1079. IF_DEBUG( USER_DATABASE) {
  1080. DBGPRINTF( ( DBG_CONTEXT,
  1081. "[%lu] Leaving USER_DATA( %08x)::Process()."
  1082. " RefCount = %d. State = %d\n",
  1083. GetTickCount(),
  1084. this, QueryReference(), QueryState())
  1085. );
  1086. }
  1087. return ( fReturn);
  1088. } // USER_DATA::ProcessAsyncIoCompletion()
  1089. # define min(a, b) (((a) < (b)) ? (a) : (b))
  1090. BOOL
  1091. USER_DATA::StartupSession(IN PVOID pvInitialRequest,
  1092. IN DWORD cbInitialRequest
  1093. )
  1094. /*++
  1095. This function allocates a buffer for receiving request from the client
  1096. and also sets up initial read from the control socket to
  1097. get client requests.
  1098. Arguments:
  1099. pvInitialRequest pointer to initial request buffer
  1100. cbInitialRequest count of bytes of data in the initial request
  1101. Returns:
  1102. TRUE on success and FALSE if there is any failure.
  1103. --*/
  1104. {
  1105. SOCKERR serr;
  1106. BOOL fReturn = FALSE;
  1107. PCSTR pszBanner;
  1108. # if DBG
  1109. if ( !IS_VALID_USER_DATA( this)) {
  1110. DBGPRINTF( ( DBG_CONTEXT,
  1111. "Encountering an invalid user data ( %08x)\n",
  1112. this));
  1113. Print();
  1114. }
  1115. # endif // DBG
  1116. DBG_ASSERT( IS_VALID_USER_DATA( this ) );
  1117. DBG_ASSERT( QueryState() == UserStateEmbryonic);
  1118. //
  1119. // Reply to the initial connection message. ( Greet the new user).
  1120. //
  1121. pszBanner = QueryInstance()->QueryBannerMsg();
  1122. if( pszBanner && *pszBanner ) {
  1123. serr = SendMultilineMessage(
  1124. REPLY_SERVICE_READY,
  1125. g_FtpServiceNameString,
  1126. TRUE,
  1127. FALSE);
  1128. serr = serr || SendMultilineMessage(
  1129. REPLY_SERVICE_READY,
  1130. pszBanner,
  1131. FALSE,
  1132. TRUE);
  1133. } else {
  1134. serr = ReplyToUser( this,
  1135. REPLY_SERVICE_READY,
  1136. "%s",
  1137. g_FtpServiceNameString );
  1138. }
  1139. if ( serr != 0) {
  1140. IF_DEBUG( ERROR) {
  1141. DBGPRINTF( ( DBG_CONTEXT,
  1142. " Cannot reply with initial connection message."
  1143. " Error = %lu\n",
  1144. serr));
  1145. }
  1146. } else {
  1147. //
  1148. // enable OOB_INLINE since we are using that for our control socket
  1149. //
  1150. BOOL fOobInline = TRUE;
  1151. serr = setsockopt( QueryControlSocket(), SOL_SOCKET,
  1152. SO_OOBINLINE, (const char *) &fOobInline,
  1153. sizeof( fOobInline));
  1154. m_cchPartialReqRecvd = 0;
  1155. if ( serr == 0) {
  1156. //
  1157. // Try to set up the buffer and enter the mode for reading
  1158. // requests from the client
  1159. //
  1160. LockUser();
  1161. if( QueryState() != UserStateDisconnected ) {
  1162. SetState( UserStateWaitingForUser);
  1163. }
  1164. UnlockUser();
  1165. if ( pvInitialRequest != NULL && cbInitialRequest > 0) {
  1166. //
  1167. // No need to issue a read, since we have the data required.
  1168. // Do a safe copy to the buffer.
  1169. //
  1170. CopyMemory( QueryReceiveBuffer(), pvInitialRequest,
  1171. min( cbInitialRequest, QueryReceiveBufferSize())
  1172. );
  1173. fReturn = TRUE;
  1174. } else {
  1175. fReturn = ReadCommand();
  1176. }
  1177. } else {
  1178. IF_DEBUG( ERROR) {
  1179. DBGPRINTF((DBG_CONTEXT,
  1180. " SetsockOpt( OOB_INLINE) failed. Error = %lu\n",
  1181. WSAGetLastError()));
  1182. }
  1183. }
  1184. }
  1185. IF_DEBUG( CLIENT) {
  1186. DWORD dwError = (fReturn) ? NO_ERROR : GetLastError();
  1187. DBGPRINTF( ( DBG_CONTEXT,
  1188. " connection ( %08x)::StartupSession() returns %d."
  1189. " Error = %lu\n",
  1190. this, fReturn,
  1191. dwError));
  1192. if (fReturn) { SetLastError( dwError); }
  1193. }
  1194. return ( fReturn);
  1195. } // USER_DATA::StartupSession()
  1196. VOID
  1197. CheckAndProcessAbortOperation( IN LPUSER_DATA pUserData)
  1198. {
  1199. if ( TEST_UF( pUserData, OOB_ABORT)) {
  1200. //
  1201. // An abort was requested by client. So our processing
  1202. // has unwound and we are supposed to send some message
  1203. // to the client. ==> simulate processing ABOR command
  1204. // ABORT was not processed yet; so process now.
  1205. //
  1206. DBGPRINTF((DBG_CONTEXT,
  1207. "Executing simulated Abort for %08x\n",
  1208. pUserData));
  1209. FacIncrement( FacSimulatedAborts);
  1210. // To avoid thread races, check twice.
  1211. if ( TEST_UF( pUserData, OOB_ABORT)) {
  1212. //
  1213. // we need this stack variable (szAbort), so that
  1214. // ParseCommand() can freely modify the string!
  1215. CHAR szAbort[10];
  1216. CLEAR_UF( pUserData, OOB_ABORT);
  1217. CopyMemory( szAbort, "ABOR", sizeof("ABOR"));
  1218. ParseCommand( pUserData, szAbort);
  1219. }
  1220. }
  1221. return;
  1222. } // CheckAndProcessAbortOperation()
  1223. BOOL
  1224. USER_DATA::ParseAndProcessRequest(IN DWORD cchRequest)
  1225. /*++
  1226. This function parses the incoming request from client, identifies the
  1227. command to execute and executes the same.
  1228. Before parsing, the input is pre-processed to remove any of telnet commands
  1229. or OOB_inlined data.
  1230. Arguments:
  1231. cchRequest count of characters of request received.
  1232. --*/
  1233. {
  1234. BOOL fLineEnded = FALSE;
  1235. DWORD cchRequestRecvd = 0;
  1236. CHAR szCommandLine[ MAX_COMMAND_LENGTH + 1];
  1237. # if DBG
  1238. if ( !IS_VALID_USER_DATA( this))
  1239. {
  1240. DBGPRINTF( ( DBG_CONTEXT,
  1241. "Encountering an invalid user data ( %08x)\n",
  1242. this));
  1243. Print();
  1244. }
  1245. # endif // DBG
  1246. DBG_ASSERT( IS_VALID_USER_DATA( this ) );
  1247. IF_DEBUG( CLIENT)
  1248. {
  1249. DBGPRINTF( ( DBG_CONTEXT,
  1250. "UserData(%08x)::ParseAndProcessRequest( %d chars)\n",
  1251. this, cchRequest));
  1252. }
  1253. //
  1254. // Fast-path if we're re-processing this command, which happens in PASV mode
  1255. //
  1256. if ( QueryInFakeIOCompletion() )
  1257. {
  1258. goto FastPathLabel;
  1259. }
  1260. if ( cchRequest > 0)
  1261. {
  1262. // We have a valid request. Process it
  1263. // Update last access time
  1264. m_TimeAtLastAccess = GetCurrentTimeInSeconds();
  1265. m_pInstance->QueryStatsObj()->UpdateTotalBytesReceived(
  1266. cchRequest*sizeof(CHAR));
  1267. if ( m_cchPartialReqRecvd + cchRequest >= MAX_COMMAND_LENGTH)
  1268. {
  1269. CHAR szCmdFailed[600];
  1270. wsprintfA( szCmdFailed,
  1271. " Command is too long: Partial=%d bytes. Now=%d \n"
  1272. " UserDb(%08x) = %s from Host: %s\n",
  1273. m_cchPartialReqRecvd, cchRequest,
  1274. this, QueryUserName(), QueryClientHostName());
  1275. DBGPRINTF((DBG_CONTEXT, szCmdFailed));
  1276. DisconnectUserWithError( ERROR_BUSY);
  1277. return ( TRUE); // we are done with this connection.
  1278. }
  1279. CopyMemory(szCommandLine, m_recvBuffer,
  1280. m_cchPartialReqRecvd + cchRequest);
  1281. szCommandLine[m_cchPartialReqRecvd + cchRequest] = '\0';
  1282. if ( !::FilterTelnetCommands(szCommandLine,
  1283. m_cchPartialReqRecvd + cchRequest,
  1284. &fLineEnded, &cchRequestRecvd))
  1285. {
  1286. if ( TEST_UF( this, TRANSFER))
  1287. {
  1288. //
  1289. // I am in data transfer mode. Some other thread is sending
  1290. // data for this client. Just post a OOB_DATA and OOB_ABORT
  1291. // OOB_DATA will cause the call-stack of other thread to unwind
  1292. // and get out of the command.
  1293. // Then check if any async transfer was occuring. If so
  1294. // process abort with disconnect now.
  1295. //
  1296. SET_UF_BITS( this, (UF_OOB_DATA | UF_OOB_ABORT));
  1297. if ( TEST_UF( this, ASYNC_TRANSFER))
  1298. {
  1299. //
  1300. // An async transfer is occuring. Stop it
  1301. //
  1302. DestroyDataConnection( ERROR_OPERATION_ABORTED);
  1303. CheckAndProcessAbortOperation( this);
  1304. }
  1305. # ifdef CHECK_DBG
  1306. Print( " OOB_ABORT ");
  1307. # endif // CHECK_DBG
  1308. IF_DEBUG( CLIENT) {
  1309. DBGPRINTF((DBG_CONTEXT,
  1310. "[%08x]Set up the implied ABORT command\n",
  1311. this));
  1312. }
  1313. IF_DEBUG( COMMANDS) {
  1314. DBGPRINTF((DBG_CONTEXT, " ***** [%08x] OOB_ABORT Set \n",
  1315. this));
  1316. }
  1317. // Ignore the rest of the commands that may have come in.
  1318. }
  1319. else
  1320. {
  1321. //
  1322. // Since no command is getting processed.
  1323. // atleast process the abort command, otherwise clients hang.
  1324. //
  1325. //
  1326. // we need this stack variable (szAbort), so that
  1327. // ParseCommand() can freely modify the string!
  1328. CHAR szAbort[10];
  1329. CopyMemory( szAbort, "ABOR", sizeof("ABOR"));
  1330. ParseCommand( this, szAbort);
  1331. CLEAR_UF( this, OOB_ABORT); // clear the abort flag!
  1332. }
  1333. }
  1334. else
  1335. {
  1336. if ( TEST_UF( this, TRANSFER))
  1337. {
  1338. //
  1339. // we are transferring data, sorry no more commands accepted.
  1340. // This could hang clients. Hey! they asked for it :( NYI
  1341. //
  1342. // Do nothing
  1343. IF_DEBUG( COMMANDS) {
  1344. DBGPRINTF((DBG_CONTEXT,
  1345. "***** [%08x] Received Request %s during"
  1346. " transfer in progress\n",
  1347. this, szCommandLine));
  1348. }
  1349. }
  1350. else
  1351. {
  1352. //
  1353. // Let ParseCommand do the dirty work.
  1354. //
  1355. // Remember the count of partial bytes received.
  1356. m_cchPartialReqRecvd = cchRequestRecvd;
  1357. if ( !fLineEnded)
  1358. {
  1359. // In case if command was long enough to fill all buffer but
  1360. // we haven't found new line simply tell to user about the error
  1361. // and disconnect. Some ftp clients will not see that msg, becuase
  1362. // connection was disconnected, but thats a bug in client code
  1363. if ( m_cchPartialReqRecvd >= MAX_COMMAND_LENGTH - 1)
  1364. {
  1365. ReplyToUser( this,
  1366. REPLY_UNRECOGNIZED_COMMAND,
  1367. PSZ_COMMAND_TOO_LONG);
  1368. DisconnectUserWithError( ERROR_BUSY );
  1369. return ( TRUE); // we are done with this connection.
  1370. }
  1371. //
  1372. // Complete line is not received. Continue reading
  1373. // the requests, till we receive the complete request
  1374. //
  1375. }
  1376. else
  1377. {
  1378. StartProcessingTimer();
  1379. //
  1380. // set the partial received byte count to zero.
  1381. // we will not use this value till next incomplete request
  1382. //
  1383. m_cchPartialReqRecvd = 0;
  1384. FastPathLabel:
  1385. ParseCommand( this, ( QueryInFakeIOCompletion() ? QueryCmdString() :
  1386. szCommandLine ) );
  1387. CheckAndProcessAbortOperation( this);
  1388. } // if TRANSFER is not there...
  1389. } //Parse if complete
  1390. } // if FilterTelnetCommands()
  1391. }
  1392. else
  1393. {
  1394. // if (cchRequest <= 0)
  1395. SET_UF( this, CONTROL_ZERO);
  1396. //
  1397. // after a quit a client is expected to wait for quit message from
  1398. // the server. if the client prematurely closes connection, then
  1399. // the server receives it as a receive with zero byte read.
  1400. // since, we should not be having outstanding read at this time,
  1401. // atq should not be calling us. On the contrary we are getting
  1402. // called by ATQ. Let us track this down.
  1403. //
  1404. if ( !TEST_UF( this, CONTROL_QUIT))
  1405. {
  1406. DisconnectUserWithError( NO_ERROR);
  1407. }
  1408. else
  1409. {
  1410. // Quit message is received and then ZeroBytes Received!!
  1411. DBGPRINTF((DBG_CONTEXT,
  1412. " (%08x)::ZeroBytes recvd after QUIT message!!."
  1413. " State = %d(%x), Ref = %d\n",
  1414. this,
  1415. QueryState(), Flags,
  1416. QueryReference()
  1417. ));
  1418. // Do nothing. Since Quit will take care of cleanup
  1419. return (TRUE);
  1420. }
  1421. }
  1422. //
  1423. // If the connection is not yet disconnected, submit a read command.
  1424. // else return that everything is fine (someone had disconnected it).
  1425. //
  1426. return ( IsDisconnected() ? TRUE : ReadCommand());
  1427. } // USER_DATA::ParseAndProcessRequest()
  1428. BOOL
  1429. USER_DATA::ReadCommand( VOID)
  1430. {
  1431. BOOL fReturn = TRUE;
  1432. DBG_CODE(
  1433. if ( !IS_VALID_USER_DATA( this)) {
  1434. DBGPRINTF( ( DBG_CONTEXT,
  1435. "Encountering an invalid user data ( %08x)\n",
  1436. this));
  1437. Print();
  1438. }
  1439. );
  1440. DBG_ASSERT( IS_VALID_USER_DATA( this ) );
  1441. if ( TEST_UF( this, CONTROL_TIMEDOUT) || IsDisconnected()) {
  1442. SetLastError( ERROR_SEM_TIMEOUT);
  1443. return (FALSE);
  1444. }
  1445. //
  1446. // Submit a read on control socket only if there is none pending!
  1447. // Otherwise, behave in idempotent manner.
  1448. //
  1449. if ( !TEST_UF( this, CONTROL_READ)) {
  1450. Reference(); // since we are going to set up async read.
  1451. InterlockedIncrement( &m_nControlRead);
  1452. DBG_ASSERT( m_nControlRead <= 1);
  1453. SET_UF( this, CONTROL_READ); // a read will be pending
  1454. if ( !m_AioControlConnection.ReadFile(QueryReceiveBuffer(),
  1455. QueryReceiveBufferSize())
  1456. ) {
  1457. CLEAR_UF( this, CONTROL_READ); // since read failed.
  1458. DBG_REQUIRE( DeReference() > 0);
  1459. InterlockedDecrement( &m_nControlRead);
  1460. DWORD dwError = GetLastError();
  1461. IF_DEBUG( ERROR) {
  1462. DBGPRINTF( ( DBG_CONTEXT,
  1463. " User( %08x)::ReadCommand() failed. Ref = %d."
  1464. " Error = %d\n",
  1465. this, QueryReference(), dwError));
  1466. }
  1467. SetLastError( dwError);
  1468. fReturn = FALSE;
  1469. }
  1470. }
  1471. return ( fReturn);
  1472. } // USER_DATA::ReadCommand()
  1473. BOOL
  1474. USER_DATA::DisconnectUserWithError(IN DWORD dwError,
  1475. IN BOOL fNextMsg OPTIONAL)
  1476. /*++
  1477. This function disconnects a user with the error code provided.
  1478. It closes down the control connection by stopping ASYNC_IO.
  1479. If the fNextMsg is not set, then it also decrements the reference count
  1480. for the user data object, to be freed soon.
  1481. --*/
  1482. {
  1483. CHAR szBuffer[120];
  1484. # if DBG
  1485. if ( !IS_VALID_USER_DATA( this)) {
  1486. DBGPRINTF( ( DBG_CONTEXT,
  1487. "Encountering an invalid user data ( %08x)\n",
  1488. this));
  1489. Print();
  1490. }
  1491. # endif // DBG
  1492. DBG_ASSERT( IS_VALID_USER_DATA( this ) );
  1493. IF_DEBUG ( CLIENT) {
  1494. DBGPRINTF( ( DBG_CONTEXT,
  1495. " USER_DATA( %08x)::DisconnectUserWithError( %lu, %d)."
  1496. " RefCount = %d\n",
  1497. this, dwError, fNextMsg, QueryReference()));
  1498. }
  1499. if (!fNextMsg) {
  1500. RemoveActiveReference();
  1501. }
  1502. LockUser();
  1503. if ( QueryState() == UserStateDisconnected) {
  1504. //
  1505. // It is already in disconnected state. Do nothing for disconnect.
  1506. //
  1507. UnlockUser();
  1508. } else {
  1509. SetState( UserStateDisconnected );
  1510. UnlockUser();
  1511. if( dwError == ERROR_SEM_TIMEOUT) {
  1512. const CHAR * apszSubStrings[3];
  1513. IF_DEBUG( CLIENT )
  1514. {
  1515. DBGPRINTF(( DBG_CONTEXT,
  1516. "client (%08x) timed-out\n", this ));
  1517. }
  1518. sprintf( szBuffer, "%lu", m_pInstance->QueryConnectionTimeout() );
  1519. apszSubStrings[0] = QueryUserName();
  1520. apszSubStrings[1] = inet_ntoa( HostIpAddress );
  1521. apszSubStrings[2] = szBuffer;
  1522. g_pInetSvc->LogEvent( FTPD_EVENT_CLIENT_TIMEOUT,
  1523. 3,
  1524. apszSubStrings,
  1525. 0 );
  1526. ReplyToUser(this,
  1527. REPLY_SERVICE_NOT_AVAILABLE,
  1528. "Timeout (%lu seconds): closing control connection.",
  1529. m_pInstance->QueryConnectionTimeout() );
  1530. }
  1531. if ( dwError != NO_ERROR) {
  1532. # ifdef CHECK_DBG
  1533. sprintf( szBuffer, " Control Socket Error=%u ", dwError);
  1534. Print( szBuffer);
  1535. # endif // CHECK_DBG
  1536. if( dwError != ERROR_SEM_TIMEOUT ) {
  1537. SetLastReplyCode( REPLY_TRANSFER_ABORTED );
  1538. }
  1539. // Produce a log record indicating the cause for failure.
  1540. WriteLogRecord( PSZ_CONNECTION_CLOSED_VERB, "", dwError);
  1541. }
  1542. //
  1543. // Force close the connection's sockets. This will cause the
  1544. // thread to awaken from any blocked socket operation. It
  1545. // is the destructor's responsibility to do any further cleanup.
  1546. // (such as calling UserDereference()).
  1547. //
  1548. CloseSockets(dwError != NO_ERROR);
  1549. }
  1550. return ( TRUE);
  1551. } // USER_DATA::DisconnectUserWithError()
  1552. static BOOL
  1553. DisconnectUserWorker( IN LPUSER_DATA pUserData, IN LPVOID pContext)
  1554. /*++
  1555. This disconnects (logically) a user connection, by resetting the
  1556. control connection and stopping IO. Later on the blown away socket
  1557. will cause an ATQ relinquish to occur to blow away of this connection.
  1558. Arguments:
  1559. pUserData pointer to User data object for connection to be disconnected.
  1560. pContext pointer to context information
  1561. ( in this case to DWORD containing error code indicating reasong for
  1562. disconnect).
  1563. Returns:
  1564. TRUE on success and FALSE if there is any failure.
  1565. --*/
  1566. {
  1567. DWORD dwError;
  1568. BOOL retVal;
  1569. DBG_ASSERT( pContext != NULL && pUserData != NULL);
  1570. DBG_ASSERT( IS_VALID_USER_DATA( pUserData ) );
  1571. dwError = *(LPDWORD ) pContext;
  1572. retVal = pUserData->DisconnectUserWithError( dwError, TRUE);
  1573. // fix for bug 268175 : if we disconnected user we need to do normal cleanup
  1574. // for that connection
  1575. // this check is not very necessary but I leave it for future
  1576. // DisconnectUserWithError always returns TRUE
  1577. if (retVal)
  1578. {
  1579. DereferenceUserDataAndKill(pUserData);
  1580. }
  1581. return retVal;
  1582. } // DisconnectUserWorker()
  1583. BOOL
  1584. DisconnectUser( IN DWORD UserId, FTP_SERVER_INSTANCE *pInstance )
  1585. /*++
  1586. This function disconnects a specified user identified using the UserId.
  1587. If UserId specified == 0, then all the users will be disconnected.
  1588. Arguments:
  1589. UserId user id for the connection to be disconnected.
  1590. Returns:
  1591. TRUE if atleast one of the connections is disconnected.
  1592. FALSE if no user connetion found.
  1593. History:
  1594. 06-April-1995 Created.
  1595. --*/
  1596. {
  1597. BOOL fFound;
  1598. DWORD dwError = ERROR_SERVER_DISABLED;
  1599. pInstance->Reference();
  1600. pInstance->LockConnectionsList();
  1601. fFound = ( pInstance->
  1602. EnumerateConnection( DisconnectUserWorker,
  1603. (LPVOID ) &dwError,
  1604. UserId));
  1605. pInstance->UnlockConnectionsList();
  1606. pInstance->Dereference();
  1607. IF_DEBUG( CLIENT) {
  1608. DWORD dwError = (fFound) ? NO_ERROR: GetLastError();
  1609. DBGPRINTF( ( DBG_CONTEXT,
  1610. " DisconnectUser( %d) returns %d. Error = %lu\n",
  1611. UserId, fFound, dwError));
  1612. if (fFound) { SetLastError( dwError); }
  1613. }
  1614. return ( fFound);
  1615. } // DisconnectUser()
  1616. static BOOL
  1617. DisconnectUserWithNoAccessWorker( IN LPUSER_DATA pUserData,
  1618. IN LPVOID pContext)
  1619. /*++
  1620. This disconnects (logically) a user connection with no access.
  1621. This occurs by resetting the control connection and stopping IO.
  1622. Later on the blown away thread
  1623. will cause an ATQ relinquish to occur to blow away of this connection.
  1624. Arguments:
  1625. pUserData pointer to User data object for connection to be disconnected.
  1626. pContext pointer to context information
  1627. ( in this case to DWORD containing error code indicating reasong for
  1628. disconnect).
  1629. Returns:
  1630. TRUE on success and FALSE if there is any failure.
  1631. --*/
  1632. {
  1633. BOOL fSuccess = TRUE;
  1634. DBG_ASSERT( pUserData != NULL);
  1635. // Ignode the pContext information.
  1636. DBG_ASSERT( IS_VALID_USER_DATA( pUserData ) );
  1637. //
  1638. // We're only interested in connected users.
  1639. //
  1640. if( pUserData->IsLoggedOn()) {
  1641. //
  1642. // If this user no longer has access to their
  1643. // current directory, blow them away.
  1644. //
  1645. if( !pUserData->VirtualPathAccessCheck(AccessTypeRead )) {
  1646. const CHAR * apszSubStrings[2];
  1647. IF_DEBUG( SECURITY ) {
  1648. DBGPRINTF(( DBG_CONTEXT,
  1649. "User %s (%lu) @ %08lX retroactively"
  1650. " denied access to %s\n",
  1651. pUserData->QueryUserName(),
  1652. pUserData->QueryId(),
  1653. pUserData,
  1654. pUserData->QueryCurrentDirectory() ));
  1655. }
  1656. fSuccess = ( pUserData->
  1657. DisconnectUserWithError(ERROR_ACCESS_DENIED,
  1658. TRUE)
  1659. );
  1660. //
  1661. // Log an event to tell the admin what happened.
  1662. //
  1663. apszSubStrings[0] = pUserData->QueryUserName();
  1664. apszSubStrings[1] = pUserData->QueryCurrentDirectory();
  1665. g_pInetSvc->LogEvent( FTPD_EVENT_RETRO_ACCESS_DENIED,
  1666. 2,
  1667. apszSubStrings,
  1668. 0 );
  1669. } // no access
  1670. } // logged on user
  1671. IF_DEBUG( CLIENT) {
  1672. DWORD dwError = (fSuccess) ? NO_ERROR: GetLastError();
  1673. DBGPRINTF( ( DBG_CONTEXT,
  1674. " DisconnectUsersWithNoAccessWorker( %d) returns %d."
  1675. " Error = %lu\n",
  1676. pUserData->QueryId(), fSuccess,
  1677. dwError)
  1678. );
  1679. if (fSuccess) { SetLastError( dwError); }
  1680. }
  1681. return ( fSuccess);
  1682. } // DisconnectUserWithNoAccessWorker()
  1683. VOID
  1684. DisconnectUsersWithNoAccess(FTP_SERVER_INSTANCE *pInstance )
  1685. /*++
  1686. This function disconnects all users who do not have read access to
  1687. their current directory. This is typically called when the access masks
  1688. have been changed.
  1689. Arguments:
  1690. None
  1691. Returns:
  1692. None.
  1693. --*/
  1694. {
  1695. BOOL fFound;
  1696. DWORD dwError = ERROR_ACCESS_DENIED;
  1697. pInstance->Reference();
  1698. pInstance->LockConnectionsList();
  1699. fFound = ( pInstance->
  1700. EnumerateConnection( DisconnectUserWithNoAccessWorker,
  1701. (LPVOID ) &dwError,
  1702. 0));
  1703. pInstance->UnlockConnectionsList();
  1704. pInstance->Dereference();
  1705. IF_DEBUG( CLIENT) {
  1706. DWORD dwError = (fFound) ? NO_ERROR: GetLastError();
  1707. DBGPRINTF( ( DBG_CONTEXT,
  1708. " DisconnectUsersWithNoAccess() returns %d."
  1709. " Error = %lu\n",
  1710. fFound, dwError)
  1711. );
  1712. if (fFound) { SetLastError( dwError); }
  1713. }
  1714. } // DisconnectUsersWithNoAccess
  1715. /*++
  1716. The following structure UserEnumBuffer is required to carry the context
  1717. information for enumerating the users currently connected.
  1718. It contains a pointer to array of USER_INFO structures which contain the
  1719. specific information for the user. The user name is stored in the buffer
  1720. from the end ( so that null terminated strings are formed back to back.
  1721. This permits efficient storage of variable length strings.
  1722. The member fResult is used to carry forward the partial result of
  1723. success/failure from one user to another ( since the enumeration has
  1724. to walk through all the elements to find out all user information).
  1725. History: MuraliK ( 12-April-1995)
  1726. --*/
  1727. struct USER_ENUM_BUFFER {
  1728. DWORD cbSize; // pointer to dword containing size of
  1729. IIS_USER_INFO_1 * pUserInfo; // pointer to start of array of USER_INFO
  1730. DWORD cbRequired; // incremental count of bytes required.
  1731. DWORD nEntry; // number of current entry ( index into pUserInfo)
  1732. DWORD dwCurrentTime; // current time
  1733. WCHAR * pszNext; // pointer to next string location.
  1734. BOOL fResult; // boolean flag accumulating partial results
  1735. };
  1736. typedef USER_ENUM_BUFFER * PUSER_ENUM_BUFFER;
  1737. BOOL
  1738. EnumerateUserInBufferWorker( IN LPUSER_DATA pUserData,
  1739. IN LPVOID pContext)
  1740. {
  1741. # ifdef CHECK_DBG
  1742. CHAR szBuffer[400];
  1743. # endif // CHECK_DBG
  1744. PUSER_ENUM_BUFFER pUserEnumBuffer = (PUSER_ENUM_BUFFER ) pContext;
  1745. DWORD tConnect;
  1746. DWORD cbUserName;
  1747. LPDWORD pcbBuffer;
  1748. DBG_ASSERT( IS_VALID_USER_DATA( pUserData ) );
  1749. //
  1750. // We're only interested in connected users.
  1751. //
  1752. if( pUserData->IsDisconnected()) {
  1753. return ( TRUE);
  1754. }
  1755. //
  1756. // Determine required buffer size for current user.
  1757. //
  1758. cbUserName = ( strlen( pUserData->QueryUserName() ) + 1 ) * sizeof(WCHAR);
  1759. pUserEnumBuffer->cbRequired += sizeof(IIS_USER_INFO_1);
  1760. //
  1761. // If there's room for the user data, store it.
  1762. //
  1763. tConnect = ( pUserEnumBuffer->dwCurrentTime -
  1764. pUserData->QueryTimeAtConnection());
  1765. if( pUserEnumBuffer->fResult &&
  1766. ( pUserEnumBuffer->cbRequired <= pUserEnumBuffer->cbSize)
  1767. ) {
  1768. LPIIS_USER_INFO_1 pUserInfo =
  1769. &pUserEnumBuffer->pUserInfo[ pUserEnumBuffer->nEntry];
  1770. pUserInfo->idUser = pUserData->QueryId();
  1771. pUserInfo->pszUser = (WCHAR *)MIDL_user_allocate( cbUserName );
  1772. if( pUserInfo->pszUser ) {
  1773. pUserInfo->fAnonymous = ( pUserData->Flags & UF_ANONYMOUS ) != 0;
  1774. pUserInfo->inetHost = (DWORD)pUserData->HostIpAddress.s_addr;
  1775. pUserInfo->tConnect = tConnect;
  1776. if( !MultiByteToWideChar( CP_OEMCP,
  1777. 0,
  1778. pUserData->QueryUserName(),
  1779. -1,
  1780. pUserInfo->pszUser,
  1781. (int)cbUserName )
  1782. ) {
  1783. DBGPRINTF(( DBG_CONTEXT,
  1784. "MultiByteToWideChar failed???\n" ));
  1785. pUserEnumBuffer->fResult = ( pUserEnumBuffer->fResult && FALSE);
  1786. } else {
  1787. pUserEnumBuffer->nEntry++;
  1788. }
  1789. }
  1790. else {
  1791. //
  1792. // Unable to allocate memory
  1793. //
  1794. pUserEnumBuffer->fResult = ( pUserEnumBuffer->fResult && FALSE);
  1795. }
  1796. } else {
  1797. pUserEnumBuffer->fResult = ( pUserEnumBuffer->fResult && FALSE);
  1798. }
  1799. # ifdef CHECK_DBG
  1800. sprintf( szBuffer, " Enum tLastAction=%u; tConnect=%u. " ,
  1801. ( pUserEnumBuffer->dwCurrentTime -
  1802. pUserData->QueryTimeAtLastAccess()),
  1803. tConnect
  1804. );
  1805. pUserData->Print( szBuffer);
  1806. # endif // CHECK_DBG
  1807. return ( TRUE);
  1808. } // EnumerateUserInBufferWorker()
  1809. BOOL
  1810. EnumerateUsers(
  1811. PCHAR pBuffer,
  1812. PDWORD pcbBuffer,
  1813. PDWORD nRead,
  1814. FTP_SERVER_INSTANCE *pInstance
  1815. )
  1816. /*++
  1817. Enumerates the current active users into the specified buffer.
  1818. Arguments:
  1819. pvEnum pointer to enumeration buffer which will receive the number of
  1820. entries and the user information.
  1821. pcbBuffer pointer to count of bytes. On entry this contains the size in
  1822. bytes of the enumeration buffer. It receives the count
  1823. of bytes for enumerating all the users.
  1824. nRead - pointer to a DWORD to return the number of user entries filled.
  1825. Returns:
  1826. TRUE if enumeration is successful ( all connected users accounted for)
  1827. FALSE otherwise
  1828. --*/
  1829. {
  1830. USER_ENUM_BUFFER userEnumBuffer;
  1831. BOOL fSuccess;
  1832. DBG_ASSERT( pcbBuffer != NULL );
  1833. IF_DEBUG( USER_DATABASE) {
  1834. DBGPRINTF( ( DBG_CONTEXT,
  1835. " Entering EnumerateUsers( %08x, %08x[%d]).\n",
  1836. pBuffer, pcbBuffer, *pcbBuffer));
  1837. }
  1838. //
  1839. // Setup the data in user enumeration buffer.
  1840. //
  1841. userEnumBuffer.cbSize = *pcbBuffer;
  1842. userEnumBuffer.cbRequired = 0;
  1843. userEnumBuffer.pUserInfo = (LPIIS_USER_INFO_1)pBuffer;
  1844. userEnumBuffer.nEntry = 0;
  1845. userEnumBuffer.dwCurrentTime = GetCurrentTimeInSeconds();
  1846. userEnumBuffer.fResult = TRUE;
  1847. //
  1848. // CODEWORK
  1849. // This field is obsolete it now points to the extra CONN_LEEWAY
  1850. // buffer.
  1851. //
  1852. userEnumBuffer.pszNext = ((WCHAR *)( pBuffer + *pcbBuffer));
  1853. //
  1854. // Scan the users and get the information required.
  1855. //
  1856. pInstance->Reference();
  1857. pInstance->LockConnectionsList();
  1858. fSuccess = (pInstance->
  1859. EnumerateConnection( EnumerateUserInBufferWorker,
  1860. (LPVOID ) &userEnumBuffer,
  1861. 0));
  1862. pInstance->UnlockConnectionsList();
  1863. pInstance->Dereference();
  1864. //
  1865. // Update enum buffer header.
  1866. //
  1867. *nRead = userEnumBuffer.nEntry;
  1868. *pcbBuffer = userEnumBuffer.cbRequired;
  1869. IF_DEBUG( USER_DATABASE) {
  1870. DBGPRINTF((DBG_CONTEXT,
  1871. " Leaving EnumerateUsers() with %d."
  1872. " Entries read =%d. BufferSize required = %d\n",
  1873. userEnumBuffer.fResult,
  1874. userEnumBuffer.nEntry, userEnumBuffer.cbRequired));
  1875. }
  1876. return ( userEnumBuffer.fResult);
  1877. } // EnumerateUsers
  1878. SOCKERR
  1879. USER_DATA::SendMultilineMessage(
  1880. IN UINT nReplyCode,
  1881. IN LPCSTR pszzMessage,
  1882. IN BOOL fIsFirst,
  1883. IN BOOL fIsLast)
  1884. /*++
  1885. Sends a multiline message to the control socket of the client.
  1886. Arguments:
  1887. nReplyCode the reply code to use for the first line of the multi-line
  1888. message.
  1889. pszzMessage pointer to double null terminated sequence of strings
  1890. containing the message to be sent.
  1891. fIsFirst flag to indicate we are starting the multiline reply. if FALSE,
  1892. don't print the code for the first line, as it was already emmited elsewhere
  1893. fIsLast flag to indicate we are finishing the multiline reply. if FALSE,
  1894. don't print the code for the first line, as it was already emmited elsewhere
  1895. If the message is empty, we do not print anything. If there is only one line, then if
  1896. fIsLast is TRUE, we only print the terminating line, otherwise we do print the openning
  1897. line if fIsFirst is TRUE.
  1898. Returns:
  1899. SOCKERR - 0 if successful, !0 if not.
  1900. History:
  1901. MuraliK 12-April-1995
  1902. --*/
  1903. {
  1904. SOCKERR serr = 0;
  1905. LPCSTR pszMsg, pszNext;
  1906. //
  1907. // return if there is nothing to send
  1908. //
  1909. if ( pszzMessage == NULL || *pszzMessage == '\0') {
  1910. return serr;
  1911. }
  1912. for ( pszMsg = pszzMessage; serr == 0 && *pszMsg != '\0'; pszMsg = pszNext) {
  1913. //
  1914. // find next message so that we can check of pszMsg is the last line
  1915. //
  1916. pszNext = pszMsg + strlen( pszMsg) + 1;
  1917. if( fIsLast && *pszNext == '\0' ) {
  1918. //
  1919. // This is globally the last line. Print it pefixed with the reply code.
  1920. //
  1921. serr = SockPrintf2(this, QueryControlSocket(),
  1922. "%u %s",
  1923. nReplyCode,
  1924. pszMsg);
  1925. } else if( fIsFirst ) {
  1926. //
  1927. // this is globally the first line of reply, and it is not globally the last one.
  1928. // print it with '-'.
  1929. //
  1930. serr = SockPrintf2(this, QueryControlSocket(),
  1931. "%u-%s",
  1932. nReplyCode,
  1933. pszMsg);
  1934. fIsFirst = FALSE;
  1935. } else {
  1936. //
  1937. // this is either an intermediate line, or the last line in this batch (but
  1938. // not globally), so print it idented without the reply code.
  1939. //
  1940. serr = SockPrintf2(this, QueryControlSocket(),
  1941. " %s",
  1942. pszMsg);
  1943. }
  1944. } // for
  1945. return ( serr);
  1946. } // USER_DATA::SendMultilineMessge()
  1947. SOCKERR
  1948. USER_DATA::SendDirectoryAnnotation( IN UINT ReplyCode, IN BOOL fIsFirst)
  1949. /*++
  1950. SYNOPSIS: Tries to open the FTPD_ANNOTATION_FILE (~~ftpsvc~~.ckm)
  1951. file in the user's current directory. If it can be
  1952. opened, it is sent to the user over the command socket
  1953. as a multi-line reply.
  1954. ENTRY:
  1955. ReplyCode - The reply code to send as the first line
  1956. of this multi-line reply.
  1957. fIsFirst - flag to indicate if this is the first line in the multi-line
  1958. reply. If not, the ReplyCode is not shown
  1959. RETURNS: SOCKERR - 0 if successful, !0 if not.
  1960. HISTORY:
  1961. KeithMo 06-May-1993 Created.
  1962. MuraliK 12-Apr-1995 Made it to be part of USER_DATA
  1963. --*/
  1964. {
  1965. FILE * pfile;
  1966. SOCKERR serr = 0;
  1967. CHAR szLine[MAX_REPLY_LENGTH+1];
  1968. //
  1969. // Try to open the annotation file.
  1970. //
  1971. pfile = Virtual_fopen( this,
  1972. FTPD_ANNOTATION_FILE,
  1973. "r" );
  1974. if( pfile == NULL )
  1975. {
  1976. //
  1977. // File not found. Blow it off.
  1978. //
  1979. return 0;
  1980. }
  1981. // protection agians attack when CKM file islarge, somebody is downloading it
  1982. // slowly on many connections and uses all ATQ threads. Note that attack is still possible
  1983. // but much more difficult to achieve
  1984. AtqSetInfo( AtqIncMaxPoolThreads, 0);
  1985. //
  1986. // While there's more text in the file, blast
  1987. // it to the user.
  1988. //
  1989. while( fgets( szLine, MAX_REPLY_LENGTH, pfile ) != NULL )
  1990. {
  1991. CHAR * pszTmp = szLine + strlen(szLine) - 1;
  1992. //
  1993. // Remove any trailing CR/LFs in the string.
  1994. //
  1995. while( ( pszTmp >= szLine ) &&
  1996. ( ( *pszTmp == '\n' ) || ( *pszTmp == '\r' ) ) )
  1997. {
  1998. *pszTmp-- = '\0';
  1999. }
  2000. //
  2001. // Ensure we send the proper prefix for the
  2002. // very *first* line of the file.
  2003. //
  2004. if( fIsFirst )
  2005. {
  2006. serr = SockPrintf2(this,
  2007. QueryControlSocket(),
  2008. "%u-%s",
  2009. ReplyCode,
  2010. szLine );
  2011. fIsFirst = FALSE;
  2012. }
  2013. else
  2014. {
  2015. serr = SockPrintf2(this,
  2016. QueryControlSocket(),
  2017. " %s",
  2018. szLine );
  2019. }
  2020. if( serr != 0 )
  2021. {
  2022. //
  2023. // Socket error sending file.
  2024. //
  2025. break;
  2026. }
  2027. }
  2028. AtqSetInfo( AtqDecMaxPoolThreads, 0);
  2029. //
  2030. // Cleanup.
  2031. //
  2032. if ( 0 != fclose( pfile )) {
  2033. IF_DEBUG( ERROR) {
  2034. DBGPRINTF(( DBG_CONTEXT,
  2035. "[%08x]::SendAnnotationFile() file close failed. "
  2036. " Error = %d\n",
  2037. this,
  2038. GetLastError()
  2039. ));
  2040. }
  2041. }
  2042. return serr;
  2043. } // USER_DATA::SendDirectoryAnnotation()
  2044. SOCKERR
  2045. USER_DATA::SendErrorToClient(
  2046. IN LPCSTR pszPath,
  2047. IN DWORD dwError,
  2048. IN LPCSTR pszDefaultErrorMsg,
  2049. IN UINT nReplyCode
  2050. )
  2051. /*++
  2052. Send an error message indicating that the path is not found or
  2053. a particular error occured in a path.
  2054. Arguments:
  2055. sock socket to be used for synchronously sending message
  2056. pszPath pointer to path to be used.
  2057. dwError DWORD containing the error code, used for getting error text.
  2058. pszDefaultErrorMsg pointer to null-terminated string containing the
  2059. error message to be used if we can't alloc error text.
  2060. nReplyCode UINT containing the FTP reply code.
  2061. Returns:
  2062. SOCKERR. 0 if successful and !0 if failure.
  2063. --*/
  2064. {
  2065. BOOL fDelete = TRUE;
  2066. LPCSTR pszText;
  2067. APIERR serr;
  2068. DBG_ASSERT( pszPath != NULL);
  2069. pszText = AllocErrorText( dwError );
  2070. if( pszText == NULL ) {
  2071. pszText = pszDefaultErrorMsg;
  2072. fDelete = FALSE;
  2073. }
  2074. serr = ReplyToUser( this,
  2075. nReplyCode,
  2076. PSZ_FILE_ERROR,
  2077. pszPath,
  2078. pszText );
  2079. if( fDelete ) {
  2080. FreeErrorText( (char *) pszText );
  2081. }
  2082. return ( serr);
  2083. } // USER_DATA::SendErrorToClient()
  2084. BOOL
  2085. USER_DATA::FreeUserToken( VOID)
  2086. /*++
  2087. This function frees the user token if present already.
  2088. Otherwise does nothing.
  2089. --*/
  2090. {
  2091. BOOL fReturn = TRUE;
  2092. if( UserToken != NULL ) {
  2093. fReturn = TsDeleteUserToken( UserToken );
  2094. UserToken = NULL;
  2095. ::RevertToSelf();
  2096. }
  2097. return ( fReturn);
  2098. } // USER_DATA::FreeUserToken()
  2099. APIERR
  2100. USER_DATA::CdToUsersHomeDirectory(IN const CHAR * pszAnonymousName)
  2101. /*++
  2102. This function changes user's home directory.
  2103. First, a CD to the virtual root is attempted.
  2104. If this succeeds, a CD to pszUser is attempted.
  2105. If this fails, a CD to DEFAULT_SUB_DIRECTORY is attempted.
  2106. Returns:
  2107. APIERR. NO_ERROR on success.
  2108. --*/
  2109. {
  2110. APIERR err;
  2111. PCSTR pszUser;
  2112. CHAR rgchRoot[MAX_PATH];
  2113. //
  2114. // Try the top-level home directory. If this fails, bag out.
  2115. // Set and try to change directory to symbolic root.
  2116. //
  2117. m_szCurrentDirectory[0] = '\0'; // initially nothing.
  2118. m_pInstance->LockThisForRead();
  2119. DBG_ASSERT( strlen( m_pInstance->QueryRoot()) < MAX_PATH);
  2120. P_strncpy( rgchRoot, m_pInstance->QueryRoot(), MAX_PATH);
  2121. m_pInstance->UnlockThis();
  2122. err = VirtualChDir( this, rgchRoot); // change to default dir.
  2123. if ( (err == NO_ERROR) &&
  2124. (m_pInstance->QueryIsolationMode() == NoIsolation) ) {
  2125. //
  2126. // We successfully CD'd into the top-level home
  2127. // directory. Now see if we can CD into pszUser.
  2128. //
  2129. P_strncpy( rgchRoot, QueryRootDirectory(), MAX_PATH);
  2130. if ( !VirtualChDir( this, rgchRoot ) ) {
  2131. //
  2132. // Nope, try DEFAULT_SUB_DIRECTORY. If this fails, just
  2133. // hang-out at the top-level home directory.
  2134. //
  2135. P_strncpy( rgchRoot, PSZ_DEFAULT_SUB_DIRECTORY, MAX_PATH);
  2136. VirtualChDir( this, rgchRoot );
  2137. }
  2138. }
  2139. return ( err);
  2140. } // USER_DATA::CdToUsersHomeDirectory()
  2141. VOID
  2142. USER_DATA::SetRootDirectory(
  2143. IN PCSTR pszAnonymousName)
  2144. /*++
  2145. this function sets the home directory for the logging in user,
  2146. based on the current isolation mode. If we are in no-isolation mode,
  2147. the user name excluding domain name is used. In isolation mode <domain>\<user>
  2148. is used for domain users, and local\<user> for local domain users. The
  2149. anonymous user gets 'LocalUser\public' in the isolation mode, and pszAnonymousName in
  2150. the no-isolation mode. For full hosting isolatin mode, the home directory is
  2151. retrieved from the AD
  2152. Arguments:
  2153. pszUser the user name logging in
  2154. pszAnonymousName the name of the anonymous directory
  2155. Returns:
  2156. NO_ERROR on success, WinError otherwise.
  2157. --*/
  2158. {
  2159. PUCHAR puch;
  2160. PCSTR pszUserName;
  2161. // BUGBUG: handle UPN names
  2162. switch (m_pInstance->QueryIsolationMode()) {
  2163. case NoIsolation:
  2164. m_szRootDir[ 0 ] = '\\';
  2165. if( TEST_UF( this, ANONYMOUS ) ) {
  2166. strcpy( m_szRootDir + 1, pszAnonymousName);
  2167. } else {
  2168. pszUserName = (PCHAR)_mbspbrk( (PUCHAR)QueryUserName(), (PUCHAR)"/\\" );
  2169. pszUserName = (pszUserName == NULL) ? QueryUserName() : pszUserName + 1;
  2170. P_strncpy( m_szRootDir + 1, pszUserName, sizeof( m_szRootDir ) - 1);
  2171. }
  2172. break;
  2173. case StandAloneIsolation:
  2174. m_szRootDir[ 0 ] = '\\';
  2175. if( TEST_UF( this, ANONYMOUS ) ) {
  2176. memcpy( m_szRootDir + 1,
  2177. SZ_LOCALUSER_DIR,
  2178. sizeof( SZ_LOCALUSER_DIR ) - 1 );
  2179. memcpy( m_szRootDir + sizeof( SZ_LOCALUSER_DIR ),
  2180. SZ_ANONYMOUS_DIR,
  2181. sizeof( SZ_ANONYMOUS_DIR ));
  2182. } else {
  2183. if (_mbspbrk( (PUCHAR)QueryUserName(), (PUCHAR)"/\\" )) {
  2184. P_strncpy( m_szRootDir + 1, QueryUserName(), MAX_PATH - 1);
  2185. FlipSlashes( m_szRootDir );
  2186. } else {
  2187. memcpy( m_szRootDir + 1,
  2188. SZ_LOCALUSER_DIR,
  2189. sizeof( SZ_LOCALUSER_DIR ) - 1 );
  2190. P_strncpy( m_szRootDir + sizeof( SZ_LOCALUSER_DIR ),
  2191. QueryUserName(),
  2192. sizeof( m_szRootDir ) - sizeof( SZ_LOCALUSER_DIR ));
  2193. }
  2194. }
  2195. break;
  2196. case EnterpriseIsolation:
  2197. DBG_ASSERT( FALSE ); // BUGBUG: not implemented
  2198. break;
  2199. default:
  2200. DBG_ASSERT( FALSE );
  2201. }
  2202. }
  2203. APIERR
  2204. USER_DATA::OpenFileForSend( IN LPSTR pszFile)
  2205. /*++
  2206. Open an existing file for transmission using TransmitFile.
  2207. This function converts the given relative path into canonicalized full
  2208. path and opens the file through the cached file handles manager.
  2209. Arguments:
  2210. pszFile pointer to null-terminated string containing the file name
  2211. Returns:
  2212. TRUE on success and FALSE if any failure.
  2213. --*/
  2214. {
  2215. APIERR err;
  2216. CHAR szCanonPath[MAX_PATH];
  2217. DWORD cbSize = MAX_PATH*sizeof(CHAR);
  2218. CHAR szVirtualPath[MAX_PATH+1];
  2219. DWORD cchVirtualPath = MAX_PATH;
  2220. DBG_ASSERT( pszFile != NULL );
  2221. //
  2222. // Close any file we might have open now
  2223. // N.B. There shouldn't be an open file; we're just
  2224. // being careful here.
  2225. //
  2226. if (m_pOpenFileInfo) {
  2227. DBGPRINTF(( DBG_CONTEXT,
  2228. "WARNING!! Closing [%08x], before opening %s\n",
  2229. pszFile
  2230. ));
  2231. DBG_REQUIRE( CloseFileForSend() );
  2232. }
  2233. //
  2234. // Open the requested file
  2235. //
  2236. err = VirtualCanonicalize(szCanonPath,
  2237. &cbSize,
  2238. pszFile,
  2239. AccessTypeRead,
  2240. NULL,
  2241. szVirtualPath,
  2242. &cchVirtualPath);
  2243. if( err == NO_ERROR ) {
  2244. DWORD dwCreateFlags = 0;
  2245. IF_DEBUG( VIRTUAL_IO ) {
  2246. DBGPRINTF(( DBG_CONTEXT,
  2247. "Opening File: %s\n", szCanonPath ));
  2248. }
  2249. // store the virtual path name of file.
  2250. P_strncpy( m_rgchFile, szVirtualPath, sizeof(m_rgchFile));
  2251. dwCreateFlags = TS_FORBID_SHORT_NAMES | TS_NOT_IMPERSONATED ;
  2252. if ( m_pMetaData )
  2253. {
  2254. if ( m_pMetaData->QueryDoCache() )
  2255. {
  2256. dwCreateFlags |= TS_CACHING_DESIRED;
  2257. }
  2258. }
  2259. else
  2260. {
  2261. dwCreateFlags |= TS_CACHING_DESIRED;
  2262. }
  2263. m_pOpenFileInfo = TsCreateFile( m_pInstance->GetTsvcCache(),
  2264. szCanonPath,
  2265. QueryImpersonationToken(),
  2266. dwCreateFlags
  2267. );
  2268. // caching desired.
  2269. if( m_pOpenFileInfo == NULL ) {
  2270. err = GetLastError();
  2271. } else {
  2272. DWORD dwAttrib = m_pOpenFileInfo->QueryAttributes();
  2273. FacIncrement( FacFilesOpened);
  2274. DBG_ASSERT( dwAttrib != 0xffffffff);
  2275. if (dwAttrib == 0xFFFFFFFF || // invalid attributes
  2276. dwAttrib & (FILE_ATTRIBUTE_DIRECTORY |
  2277. FILE_ATTRIBUTE_HIDDEN |
  2278. FILE_ATTRIBUTE_SYSTEM)
  2279. ) {
  2280. FacIncrement( FacFilesInvalid);
  2281. err = ERROR_FILE_NOT_FOUND;
  2282. }
  2283. }
  2284. }
  2285. if( err != NO_ERROR ) {
  2286. IF_DEBUG( VIRTUAL_IO ) {
  2287. DBGPRINTF(( DBG_CONTEXT,
  2288. "cannot open %s, error %lu\n",
  2289. pszFile,
  2290. err ));
  2291. }
  2292. }
  2293. return ( err);
  2294. } // USER_DATA::OpenFileForSend()
  2295. BOOL
  2296. USER_DATA::CloseFileForSend( IN DWORD dwError)
  2297. {
  2298. BOOL fReturn = TRUE;
  2299. TS_OPEN_FILE_INFO * pOpenFileInfo;
  2300. // make sure it includes the full path
  2301. DBG_ASSERT( m_rgchFile[0] == '/');
  2302. pOpenFileInfo = (TS_OPEN_FILE_INFO *) InterlockedExchangePointer(
  2303. (PVOID *) &m_pOpenFileInfo,
  2304. NULL
  2305. );
  2306. if ( pOpenFileInfo != NULL) {
  2307. //
  2308. // Fabricate an appropriate reply code based on the incoming
  2309. // error code. WriteLogRecord() will pick up this reply code
  2310. // and use it in the activity log.
  2311. //
  2312. SetLastReplyCode(
  2313. ( dwError == NO_ERROR )
  2314. ? REPLY_TRANSFER_OK
  2315. : REPLY_TRANSFER_ABORTED
  2316. );
  2317. FacIncrement( FacFilesClosed);
  2318. TsCloseHandle( m_pInstance->GetTsvcCache(), pOpenFileInfo);
  2319. WriteLogRecord( PSZ_SENT_VERB, m_rgchFile, dwError);
  2320. }
  2321. return ( fReturn);
  2322. } // USER_DATA::CloseFileForSend()
  2323. # define MAX_ERROR_MESSAGE_LEN ( 500)
  2324. VOID
  2325. USER_DATA::WriteLogRecord( IN LPCSTR pszVerb,
  2326. IN LPCSTR pszPath,
  2327. IN DWORD dwError)
  2328. /*++
  2329. This function writes the log record for current request made to the
  2330. Ftp server by the client.
  2331. Arguments:
  2332. pszVerb - pointer to null-terminated string containing the verb
  2333. of operation done
  2334. pszPath - pointer to string containing the path for the verb
  2335. dwError - DWORD containing the error code for operation
  2336. Returns:
  2337. None.
  2338. --*/
  2339. {
  2340. INETLOG_INFORMATION ilRequest;
  2341. DWORD dwLog;
  2342. CHAR pszClientHostName[50];
  2343. CHAR pszServerIpAddress[50];
  2344. CHAR rgchRequest[MAX_PATH + 20];
  2345. DWORD cch;
  2346. static CHAR szFTPVersion[]="FTP";
  2347. BOOL fDontLog = m_pMetaData && m_pMetaData->DontLog();
  2348. if (!fDontLog)
  2349. {
  2350. //
  2351. // Fill in the information that needs to be logged.
  2352. //
  2353. ZeroMemory(&ilRequest, sizeof(ilRequest));
  2354. strcpy( pszClientHostName, (char *)QueryClientHostName());
  2355. ilRequest.pszClientHostName = pszClientHostName;
  2356. ilRequest.cbClientHostName = strlen(pszClientHostName);
  2357. ilRequest.pszClientUserName = (char *)QueryUserName();
  2358. strcpy( pszServerIpAddress, inet_ntoa( LocalIpAddress ));
  2359. ilRequest.pszServerAddress = pszServerIpAddress;
  2360. ilRequest.msTimeForProcessing = QueryProcessingTime();
  2361. ilRequest.dwBytesSent = m_licbSent.LowPart;
  2362. ilRequest.dwBytesRecvd = m_cbRecvd;
  2363. ilRequest.dwProtocolStatus = GetLastReplyCode();
  2364. ilRequest.dwWin32Status = dwError;
  2365. ilRequest.dwPort = ntohs ((WORD)LocalIpPort);
  2366. cch = wsprintfA( rgchRequest, "[%d]%s", QueryId(), pszVerb);
  2367. DBG_ASSERT( cch < MAX_PATH + 20);
  2368. ilRequest.pszOperation = rgchRequest;
  2369. if ( rgchRequest != NULL ) {
  2370. ilRequest.cbOperation = strlen(rgchRequest);
  2371. } else {
  2372. ilRequest.cbOperation = 0;
  2373. }
  2374. ilRequest.pszTarget = (char *)pszPath;
  2375. if ( pszPath != NULL ) {
  2376. ilRequest.cbTarget = strlen((char *)pszPath);
  2377. } else {
  2378. ilRequest.cbTarget = 0;
  2379. }
  2380. ilRequest.pszParameters = "";
  2381. ilRequest.pszVersion = szFTPVersion;
  2382. dwLog = m_pInstance->m_Logging.LogInformation( &ilRequest);
  2383. if ( dwLog != NO_ERROR) {
  2384. IF_DEBUG( ERROR) {
  2385. DBGPRINTF((DBG_CONTEXT,
  2386. " Unable to log information to logger. Error = %u\n",
  2387. dwLog));
  2388. DBGPRINTF((DBG_CONTEXT,
  2389. " Request From %s, User %s. Request = %s %s\n",
  2390. ilRequest.pszClientHostName,
  2391. ilRequest.pszClientUserName,
  2392. ilRequest.pszOperation,
  2393. ilRequest.pszTarget));
  2394. }
  2395. }
  2396. //
  2397. // LogInformation() should not fail.
  2398. // If it does fail, the TsvcInfo will gracefully suspend logging
  2399. // for now.
  2400. // We may want to gracefully handle the same.
  2401. //
  2402. }
  2403. m_cbRecvd = 0; // reset since we wrote the record
  2404. m_pInstance->QueryStatsObj()->UpdateTotalBytesSent( m_licbSent.QuadPart );
  2405. m_licbSent.QuadPart = 0;
  2406. return;
  2407. } // USER_DATA::WriteLogRecord()
  2408. VOID
  2409. USER_DATA::WriteLogRecordForSendError( DWORD dwError )
  2410. {
  2411. //
  2412. // We put this into its own method in this file so it can access
  2413. // the common PSZ_SENT_VERB global.
  2414. //
  2415. WriteLogRecord(
  2416. PSZ_SENT_VERB,
  2417. m_rgchFile,
  2418. dwError
  2419. );
  2420. } // USER_DATA::WriteLogRecordForSendError
  2421. //
  2422. // Private functions.
  2423. //
  2424. VOID
  2425. USER_DATA::CloseSockets(IN BOOL fWarnUser)
  2426. /*++
  2427. Closes sockets (data and control) opened by the user for this session.
  2428. Arguments:
  2429. fWarnUser - If TRUE, send the user a warning shot before closing
  2430. the sockets.
  2431. --*/
  2432. {
  2433. SOCKET PassiveSocket;
  2434. SOCKET ControlSocket;
  2435. DBG_ASSERT( IS_VALID_USER_DATA( this ) );
  2436. //
  2437. // Close any open sockets. It is very important to set
  2438. // PassiveDataListen socket & ControlSocket to INVALID_SOCKET
  2439. // *before* we actually close the sockets.
  2440. // Since this routine is called to
  2441. // disconnect a user, and may be called from the RPC thread,
  2442. // closing one of the sockets may cause the client thread
  2443. // to unblock and try to access the socket. Setting the
  2444. // values in the per-user area to INVALID_SOCKET before
  2445. // closing the sockets keeps this from being a problem.
  2446. //
  2447. // This was a problem created by the Select or WaitForMultipleObjects()
  2448. // Investigate if such race conditions occur with Asynchronous IO?
  2449. // NYI
  2450. //
  2451. CleanupPassiveSocket( TRUE );
  2452. //
  2453. // Get rid of the async io connection used for data transfer.
  2454. //
  2455. m_AioDataConnection.StopIo( NO_ERROR);
  2456. ControlSocket = QueryControlSocket();
  2457. if( ControlSocket != INVALID_SOCKET )
  2458. {
  2459. if( fWarnUser )
  2460. {
  2461. //
  2462. // Since this may be called in a context other than
  2463. // the user we're disconnecting, we cannot rely
  2464. // on the USER_DATA fields. So, we cannot call
  2465. // SockReply, so we'll kludge one together with
  2466. // SockPrintf2.
  2467. //
  2468. SockPrintf2( this,
  2469. ControlSocket,
  2470. "%d Terminating connection.",
  2471. REPLY_SERVICE_NOT_AVAILABLE );
  2472. }
  2473. StopControlIo(); // to stop the io on control socket.
  2474. }
  2475. return;
  2476. } // USER_DATA::CloseSockets()
  2477. /*******************************************************************
  2478. NAME: UserpGetNextId
  2479. SYNOPSIS: Returns the next available user id.
  2480. RETURNS: DWORD - The user id.
  2481. HISTORY:
  2482. KeithMo 23-Mar-1993 Created.
  2483. ********************************************************************/
  2484. DWORD
  2485. UserpGetNextId(
  2486. VOID
  2487. )
  2488. {
  2489. DWORD userId;
  2490. // Increment the global counter, avoiding it from becoming 0.
  2491. InterlockedIncrement( (LPLONG ) &p_NextUserId);
  2492. if ((userId = p_NextUserId) == 0) {
  2493. InterlockedIncrement( (LPLONG ) &p_NextUserId);
  2494. userId = p_NextUserId;
  2495. }
  2496. DBG_ASSERT( userId != 0);
  2497. return userId;
  2498. } // UserpGetNextId
  2499. VOID
  2500. USER_DATA::Print( IN LPCSTR pszMsg) const
  2501. /*++
  2502. Prints the UserData object in debug mode.
  2503. History:
  2504. MuraliK 28-March-1995 Created.
  2505. --*/
  2506. {
  2507. # ifdef CHECK_DBG
  2508. CHAR szBuffer[1000];
  2509. sprintf( szBuffer,
  2510. "[%d] %s: {%u} \"%s\" State=%u. Ref=%u.\n"
  2511. " Ctrl sock=%u; Atq=%x. Data sock=%u; Atq=%x. CtrlRead=%u\n"
  2512. " LastCmd= \"%s\"\n",
  2513. GetCurrentThreadId(), pszMsg,
  2514. QueryId(), QueryUserName(),
  2515. QueryState(), QueryReference(),
  2516. QueryControlSocket(), m_AioControlConnection.QueryAtqContext(),
  2517. QueryDataSocket(), m_AioDataConnection.QueryAtqContext(),
  2518. TEST_UF( this, CONTROL_READ), m_recvBuffer
  2519. );
  2520. OutputDebugString( szBuffer);
  2521. # endif // CHECK_DBG
  2522. #ifndef _NO_TRACING_
  2523. CHKINFO( ( DBG_CONTEXT,
  2524. " Printing USER_DATA( %08x) Signature: %08x\n"
  2525. " RefCount = %08x; UserState = %08x;\n"
  2526. " ControlSocket = %08x; PassiveL = %08x\n"
  2527. " FileInfo@ = %08x; CurDir( %s) Handle = %08x\n"
  2528. " UserName = %s; UserToken = %08x; UserId = %u\n"
  2529. " Behaviour Flags = %08x; XferType = %d; XferMode = %d\n",
  2530. this, Signature, m_References, UserState,
  2531. QueryControlSocket(), m_sPassiveDataListen,
  2532. m_pOpenFileInfo, QueryCurrentDirectory(), CurrentDirHandle,
  2533. QueryUserName(), UserToken, QueryId(),
  2534. Flags, m_xferType, m_xferMode));
  2535. #else
  2536. DBGPRINTF( ( DBG_CONTEXT,
  2537. " Printing USER_DATA( %08x) Signature: %08x\n"
  2538. " RefCount = %08x; UserState = %08x;\n"
  2539. " ControlSocket = %08x; PassiveL = %08x\n"
  2540. " FileInfo@ = %08x; CurDir( %s) Handle = %08x\n"
  2541. " UserName = %s; UserToken = %08x; UserId = %u\n"
  2542. " Behaviour Flags = %08x; XferType = %d; XferMode = %d\n",
  2543. this, Signature, m_References, UserState,
  2544. QueryControlSocket(), m_sPassiveDataListen,
  2545. m_pOpenFileInfo, QueryCurrentDirectory(), CurrentDirHandle,
  2546. QueryUserName(), UserToken, QueryId(),
  2547. Flags, m_xferType, m_xferMode));
  2548. #endif
  2549. DBGPRINTF( ( DBG_CONTEXT,
  2550. " Local IpAddr = %s; HostIpAddr = %s; DataIpAddr = %s;\n"
  2551. " Port = %d; TimeAtConnection = %08x;\n",
  2552. inet_ntoa( LocalIpAddress), inet_ntoa( HostIpAddress),
  2553. inet_ntoa( DataIpAddress),
  2554. DataPort,
  2555. m_TimeAtConnection));
  2556. DBGPRINTF(( DBG_CONTEXT, " ASYNC_IO_CONN Control=%08x; Data=%08x\n",
  2557. &m_AioControlConnection, m_AioDataConnection));
  2558. IF_DEBUG( ASYNC_IO) {
  2559. # if DBG
  2560. m_AioControlConnection.Print();
  2561. m_AioDataConnection.Print();
  2562. # endif // DBG
  2563. }
  2564. return;
  2565. } // USER_DATA::Print()
  2566. BOOL
  2567. USER_DATA::VirtualPathAccessCheck(IN ACCESS_TYPE _access, IN char * pszPath)
  2568. /*++
  2569. checks to see if the access is allowed for accessing the path
  2570. using pszPath after canonicalizing it.
  2571. Arguments:
  2572. access the access desired
  2573. pszPath pointer to string containing the path
  2574. Returns:
  2575. TRUE on success and FALSE if there is any failure.
  2576. --*/
  2577. {
  2578. DWORD dwError;
  2579. DWORD dwSize = MAX_PATH;
  2580. CHAR rgchPath[MAX_PATH];
  2581. // this following call converts the symbolic path into absolute
  2582. // and also does path access check.
  2583. dwError = VirtualCanonicalize(rgchPath, &dwSize,
  2584. pszPath, _access);
  2585. return ( dwError);
  2586. } // USER_DATA::VirtualPathAccessCheck()
  2587. APIERR
  2588. USER_DATA::VirtualCanonicalize(
  2589. OUT CHAR * pszDest,
  2590. IN OUT LPDWORD lpdwSize,
  2591. IN OUT CHAR * pszSearchPath,
  2592. IN ACCESS_TYPE _access,
  2593. OUT LPDWORD pdwAccessMask,
  2594. OUT CHAR * pchVirtualPath, /* OPTIONAL */
  2595. IN OUT LPDWORD lpcchVirtualPath /* OPTIONAL */
  2596. )
  2597. /*++
  2598. This function canonicalizes the path, taking into account the current
  2599. user's current directory value.
  2600. Arguments:
  2601. pszDest string that will on return contain the complete
  2602. canonicalized path. This buffer will be of size
  2603. specified in *lpdwSize.
  2604. lpdwSize Contains the size of the buffer pszDest on entry.
  2605. On return contains the number of bytes written
  2606. into the buffer or number of bytes required.
  2607. pszSearchPath pointer to string containing the path to be converted.
  2608. IF NULL, use the current directory only
  2609. accesss Access type for this path ( read, write, etc.)
  2610. pdwAccessMask pointer to DWORD which on succesful deciphering
  2611. will contain the access mask.
  2612. pchVirtualPath pointer to string which will contain the sanitized
  2613. virtual path on return (on success)
  2614. lpcchVirtualPath pointer to DWORD containing the length of buffer
  2615. (contains the length on return).
  2616. Returns:
  2617. Win32 Error Code - NO_ERROR on success
  2618. MuraliK 24-Apr-1995 Created.
  2619. --*/
  2620. {
  2621. DWORD dwError = NO_ERROR;
  2622. CHAR rgchVirtual[MAX_PATH];
  2623. DBG_ASSERT( pszDest != NULL);
  2624. DBG_ASSERT( lpdwSize != NULL);
  2625. DBG_ASSERT( pszSearchPath != NULL);
  2626. IF_DEBUG( VIRTUAL_IO) {
  2627. DBGPRINTF(( DBG_CONTEXT,
  2628. "UserData(%08x)::VirtualCanonicalize(%08x, %08x[%u],"
  2629. " %s, %d)\n",
  2630. this, pszDest, lpdwSize, *lpdwSize, pszSearchPath, _access));
  2631. }
  2632. if ( pdwAccessMask != NULL) {
  2633. *pdwAccessMask = 0;
  2634. }
  2635. //
  2636. // Form the virtual path for the given path.
  2637. //
  2638. if ( !IS_PATH_SEP( *pszSearchPath)) {
  2639. const CHAR * pszNewDir = QueryCurrentDirectory(); // get virtual dir.
  2640. //
  2641. // This is a relative path. append it to currrent directory
  2642. //
  2643. if ( strlen(pszNewDir) + strlen(pszSearchPath) + 2 <= MAX_PATH) {
  2644. // copy the current directory
  2645. wsprintfA( rgchVirtual, "%s/%s",
  2646. pszNewDir, pszSearchPath);
  2647. pszSearchPath = rgchVirtual;
  2648. } else {
  2649. // long path --> is not supported.
  2650. DBGPRINTF((DBG_CONTEXT, "Long Virtual Path %s---%s\n",
  2651. pszNewDir, pszSearchPath));
  2652. dwError = ERROR_PATH_NOT_FOUND;
  2653. }
  2654. } else {
  2655. // This is an absolute virtual path.
  2656. // need to overwrite this virtual path with absolute
  2657. // path of the root. Do nothing.
  2658. }
  2659. if ( dwError == NO_ERROR) {
  2660. DWORD dwAccessMask = 0;
  2661. DBG_ASSERT( IS_PATH_SEP(*pszSearchPath));
  2662. //
  2663. // Now we have the complete symbolic path to the target file.
  2664. // Translate it into the absolute path
  2665. //
  2666. VirtualpSanitizePath( pszSearchPath);
  2667. if ( !LookupVirtualRoot( pszSearchPath,
  2668. pszDest,
  2669. lpdwSize,
  2670. &dwAccessMask ) ) {
  2671. dwError = GetLastError();
  2672. DBGPRINTF(( DBG_CONTEXT,
  2673. "LookupVirtualRoot Failed. Error = %d. pszDest = %s. BReq=%d\n",
  2674. dwError, pszDest, *lpdwSize));
  2675. } else if ( !PathAccessCheck( _access, dwAccessMask,
  2676. TEST_UF( this, READ_ACCESS),
  2677. TEST_UF( this, WRITE_ACCESS))
  2678. ) {
  2679. dwError = GetLastError();
  2680. DBGPRINTF(( DBG_CONTEXT,
  2681. "PathAccessCheck Failed. Error = %d. pszDest = %s\n",
  2682. dwError, pszDest));
  2683. } else if ( lpcchVirtualPath != NULL) {
  2684. // successful in getting the path.
  2685. DWORD cchVPath = strlen( pszSearchPath);
  2686. if ( *lpcchVirtualPath > cchVPath && pchVirtualPath != NULL) {
  2687. // copy the virtual path, since we have space.
  2688. strcpy( pchVirtualPath, pszSearchPath);
  2689. }
  2690. *lpcchVirtualPath = cchVPath; // set the length to required size.
  2691. }
  2692. if ( dwError == NO_ERROR ) {
  2693. // IP check
  2694. AC_RESULT acIpAccess;
  2695. AC_RESULT acDnsAccess;
  2696. BOOL fNeedDnsCheck;
  2697. BindPathAccessCheck();
  2698. acIpAccess = QueryAccessCheck()->CheckIpAccess( &fNeedDnsCheck );
  2699. if ( (acIpAccess == AC_IN_DENY_LIST) ||
  2700. ((acIpAccess == AC_NOT_IN_GRANT_LIST) && !fNeedDnsCheck) ) {
  2701. dwError = ERROR_INCORRECT_ADDRESS;
  2702. }
  2703. else if ( fNeedDnsCheck ) {
  2704. if ( !QueryAccessCheck()->IsDnsResolved() ) {
  2705. BOOL fSync;
  2706. LPSTR pDns;
  2707. if ( !QueryAccessCheck()->QueryDnsName( &fSync,
  2708. (ADDRCHECKFUNCEX)NULL,
  2709. (ADDRCHECKARG)NULL,
  2710. &pDns ) ) {
  2711. dwError = ERROR_INCORRECT_ADDRESS;
  2712. }
  2713. }
  2714. if ( dwError == NO_ERROR ) {
  2715. acDnsAccess = QueryAccessCheck()->CheckDnsAccess();
  2716. if ( (acDnsAccess == AC_IN_DENY_LIST) ||
  2717. (acDnsAccess == AC_NOT_IN_GRANT_LIST) ||
  2718. ((m_acCheck == AC_NOT_IN_GRANT_LIST) &&
  2719. (acDnsAccess != AC_IN_GRANT_LIST) ) ) {
  2720. dwError = ERROR_INCORRECT_ADDRESS;
  2721. }
  2722. }
  2723. }
  2724. UnbindPathAccessCheck();
  2725. }
  2726. if ( pdwAccessMask != NULL) {
  2727. *pdwAccessMask = dwAccessMask;
  2728. }
  2729. }
  2730. IF_DEBUG( VIRTUAL_IO) {
  2731. if ( dwError != NO_ERROR) {
  2732. DBGPRINTF(( DBG_CONTEXT,
  2733. " Cannot Canonicalize %s -- %s, Error = %lu\n",
  2734. QueryCurrentDirectory(),
  2735. pszSearchPath,
  2736. dwError));
  2737. } else {
  2738. DBGPRINTF(( DBG_CONTEXT,
  2739. "Canonicalized path is: %s\n",
  2740. pszDest));
  2741. }
  2742. }
  2743. return ( dwError);
  2744. } // USER_DATA::VirtualCanonicalize()
  2745. /*******************************************************************
  2746. ********************************************************************/
  2747. SOCKERR
  2748. USER_DATA::EstablishDataConnection(
  2749. IN LPCSTR pszReason,
  2750. IN LPCSTR pszSize
  2751. )
  2752. /*++
  2753. Connects to the client's data socket.
  2754. Arguments:
  2755. pszReason - The reason for the transfer (file list, get, put, etc).
  2756. pszSize - size of data being transferred.
  2757. Returns:
  2758. socket error code on any error.
  2759. --*/
  2760. {
  2761. SOCKERR serr = 0;
  2762. SOCKET DataSocket = INVALID_SOCKET;
  2763. BOOL fPassive;
  2764. BOOL fAcceptableSocket = FALSE;
  2765. //
  2766. // if we're in passive mode and aren't dealing with a fake IO completion [ie reprocessing
  2767. // the command], we just set up the event that will get signalled when the client
  2768. // actually connects.
  2769. //
  2770. if ( TEST_UF( this, PASSIVE ) &&
  2771. !QueryInFakeIOCompletion() )
  2772. {
  2773. //
  2774. // Ensure we actually created a passive listen data socket.
  2775. // no data transfer socket is in AsyncIo object.
  2776. //
  2777. DBG_ASSERT( m_sPassiveDataListen != INVALID_SOCKET );
  2778. //
  2779. // To avoid blocking while waiting for the client to connect, we're going to use
  2780. // WSAEventSelect() to wait for the socket to be accept()'able.
  2781. //
  2782. //
  2783. if ( ( serr = AddPASVAcceptEvent( &fAcceptableSocket ) ) != 0 )
  2784. {
  2785. ReplyToUser( this,
  2786. REPLY_LOCAL_ERROR,
  2787. PSZ_TOO_MANY_PASV_USERS );
  2788. return ( serr );
  2789. }
  2790. //
  2791. // No need to wait around, we can call accept() on the socket right now
  2792. //
  2793. if ( fAcceptableSocket )
  2794. {
  2795. goto continue_label;
  2796. }
  2797. m_fWaitingForPASVConn = TRUE;
  2798. m_fHavePASVConn = FALSE;
  2799. return ERROR_IO_PENDING;
  2800. }
  2801. DBG_ASSERT( !TEST_UF(this, PASSIVE) || QueryInFakeIOCompletion() );
  2802. continue_label:
  2803. //
  2804. // Reset any oob flag.
  2805. //
  2806. CLEAR_UF( this, OOB_DATA );
  2807. //
  2808. // Capture the user's passive flag, then reset to FALSE.
  2809. //
  2810. fPassive = TEST_UF( this, PASSIVE );
  2811. CLEAR_UF( this, PASSIVE );
  2812. //
  2813. // If we're in passive mode, then accept a connection to
  2814. // the data socket.
  2815. //
  2816. // Calling accept() on this socket should -not- block because shouldn't get this
  2817. // far without being sure that calling accept() won't block - that's the point of
  2818. // jumping through the WSAEventSelect() hoops mentioned above
  2819. //
  2820. if( fPassive )
  2821. {
  2822. SOCKADDR_IN saddrClient;
  2823. //
  2824. // Ensure we actually created a passive listen data socket.
  2825. // no data transfer socket is in AsyncIo object.
  2826. //
  2827. DBG_ASSERT( m_sPassiveDataListen != INVALID_SOCKET );
  2828. //
  2829. // Wait for a connection.
  2830. //
  2831. IF_DEBUG( CLIENT )
  2832. {
  2833. DBGPRINTF(( DBG_CONTEXT,
  2834. "waiting for passive connection on socket %d\n",
  2835. m_sPassiveDataListen ));
  2836. }
  2837. serr = AcceptSocket( m_sPassiveDataListen,
  2838. &DataSocket,
  2839. &saddrClient,
  2840. TRUE,
  2841. m_pInstance ); // enforce timeouts
  2842. //
  2843. // We can kill m_sPassiveDataListen now.
  2844. // We only allow one connection in passive mode.
  2845. //
  2846. CleanupPassiveSocket( TRUE );
  2847. // PASV Theft is disabled, so you MUST have the same IP
  2848. // address ad the Control Connection
  2849. if (!(QueryInstance()->IsEnablePasvTheft()))
  2850. {
  2851. if (!(HostIpAddress.S_un.S_addr == saddrClient.sin_addr.S_un.S_addr))
  2852. {
  2853. DBGPRINTF(( DBG_CONTEXT,
  2854. "Unmatching IP - Control: %d.%d.%d.%d Data: %d.%d.%d.%d \n",
  2855. HostIpAddress.S_un.S_un_b.s_b1,
  2856. HostIpAddress.S_un.S_un_b.s_b2,
  2857. HostIpAddress.S_un.S_un_b.s_b3,
  2858. HostIpAddress.S_un.S_un_b.s_b4,
  2859. saddrClient.sin_addr.S_un.S_un_b.s_b1,
  2860. saddrClient.sin_addr.S_un.S_un_b.s_b2,
  2861. saddrClient.sin_addr.S_un.S_un_b.s_b3,
  2862. saddrClient.sin_addr.S_un.S_un_b.s_b4));
  2863. CloseSocket( DataSocket);
  2864. DataSocket = INVALID_SOCKET;
  2865. serr = WSA_OPERATION_ABORTED;
  2866. };
  2867. };
  2868. if( serr == 0 )
  2869. {
  2870. //
  2871. // Got one.
  2872. //
  2873. DBG_ASSERT( DataSocket != INVALID_SOCKET );
  2874. m_fHavePASVConn = TRUE;
  2875. m_fWaitingForPASVConn = FALSE;
  2876. FacIncrement( FacPassiveDataConnections);
  2877. if ( m_AioDataConnection.SetNewSocket( DataSocket))
  2878. {
  2879. ReplyToUser(this,
  2880. REPLY_TRANSFER_STARTING,
  2881. PSZ_TRANSFER_STARTING);
  2882. }
  2883. else
  2884. {
  2885. //
  2886. // We are possibly running low on resources. Send error.
  2887. //
  2888. ReplyToUser( this,
  2889. REPLY_LOCAL_ERROR,
  2890. PSZ_INSUFFICIENT_RESOURCES);
  2891. CloseSocket( DataSocket);
  2892. DataSocket = INVALID_SOCKET;
  2893. serr = WSAENOBUFS;
  2894. }
  2895. }
  2896. else
  2897. {
  2898. IF_DEBUG( CLIENT )
  2899. {
  2900. DBGPRINTF(( DBG_CONTEXT,
  2901. "cannot wait for connection, error %d\n",
  2902. serr ));
  2903. }
  2904. ReplyToUser(this,
  2905. REPLY_TRANSFER_ABORTED,
  2906. PSZ_TRANSFER_ABORTED);
  2907. }
  2908. }
  2909. else
  2910. {
  2911. //
  2912. // Announce our intentions of establishing a connection.
  2913. //
  2914. ReplyToUser(this,
  2915. REPLY_OPENING_CONNECTION,
  2916. PSZ_OPENING_DATA_CONNECTION,
  2917. TransferType(m_xferType ),
  2918. pszReason,
  2919. pszSize);
  2920. //
  2921. // Open data socket.
  2922. //
  2923. serr = CreateDataSocket(&DataSocket, // Will receive socket
  2924. 0, // Local address
  2925. CONN_PORT_TO_DATA_PORT(LocalIpPort),
  2926. DataIpAddress.s_addr,// RemoteAddr
  2927. DataPort ); // Remote port
  2928. if ( serr == 0 )
  2929. {
  2930. DBG_ASSERT( DataSocket != INVALID_SOCKET );
  2931. FacIncrement( FacActiveDataConnections);
  2932. if ( !m_AioDataConnection.SetNewSocket( DataSocket))
  2933. {
  2934. CloseSocket( DataSocket);
  2935. DataSocket = INVALID_SOCKET;
  2936. serr = WSAENOBUFS;
  2937. }
  2938. }
  2939. if ( serr != 0)
  2940. {
  2941. ReplyToUser(this,
  2942. REPLY_CANNOT_OPEN_CONNECTION,
  2943. PSZ_CANNOT_OPEN_DATA_CONNECTION);
  2944. IF_DEBUG( COMMANDS )
  2945. {
  2946. DBGPRINTF(( DBG_CONTEXT,
  2947. "could not create data socket, error %d\n",
  2948. serr ));
  2949. }
  2950. }
  2951. }
  2952. if( serr == 0 )
  2953. {
  2954. // set this to indicate a transfer might start
  2955. SET_UF( this, TRANSFER );
  2956. //
  2957. // Submit a read command on control socket, since we
  2958. // have to await possibility of an abort on OOB_INLINE.
  2959. // Can we ignore possibility of an error on read request?
  2960. //
  2961. if ( !ReadCommand())
  2962. {
  2963. DWORD dwError = GetLastError();
  2964. # ifdef CHECK_DBG
  2965. CHAR szBuffer[100];
  2966. sprintf( szBuffer, " Read while DataTfr failed Error = %u. ",
  2967. dwError);
  2968. Print( szBuffer);
  2969. # endif // CHECK_DBG
  2970. IF_DEBUG(CLIENT) {
  2971. DBGPRINTF((DBG_CONTEXT,
  2972. " %08x::ReadCommand() failed. Error = %u\n",
  2973. this, dwError));
  2974. SetLastError( dwError);
  2975. }
  2976. }
  2977. }
  2978. return ( serr);
  2979. } // USER_DATA::EstablishDataConnection()
  2980. BOOL
  2981. USER_DATA::DestroyDataConnection( IN DWORD dwError)
  2982. /*++
  2983. Tears down the connection to the client's data socket that was created
  2984. using EstablishDataConnection()
  2985. Arguments:
  2986. dwError = NO_ERROR if data is transferred successfully.
  2987. Win32 error code otherwise
  2988. --*/
  2989. {
  2990. UINT replyCode;
  2991. LPCSTR pszReply;
  2992. BOOL fTransfer;
  2993. fTransfer = TEST_UF( this, TRANSFER);
  2994. CLEAR_UF( this, TRANSFER );
  2995. CleanupPASVFlags();
  2996. //
  2997. // Close the data socket.
  2998. //
  2999. DBG_ASSERT( m_sPassiveDataListen == INVALID_SOCKET);
  3000. // Stop Io occuring on data connection
  3001. m_AioDataConnection.StopIo(dwError);
  3002. if ( fTransfer) {
  3003. //
  3004. // Tell the client we're done with the transfer.
  3005. //
  3006. if ( dwError == NO_ERROR) {
  3007. replyCode = REPLY_TRANSFER_OK;
  3008. pszReply = PSZ_TRANSFER_COMPLETE;
  3009. } else {
  3010. replyCode = REPLY_TRANSFER_ABORTED;
  3011. pszReply = PSZ_TRANSFER_ABORTED;
  3012. }
  3013. ReplyToUser(this, replyCode, pszReply);
  3014. }
  3015. return (TRUE);
  3016. } // USER_DATA::DestroyDataConnection()
  3017. APIERR
  3018. USER_DATA::GetFileSize()
  3019. {
  3020. LARGE_INTEGER FileSize;
  3021. DWORD dwError = NO_ERROR;
  3022. TS_OPEN_FILE_INFO * pOpenFileInfo;
  3023. CHAR rgchSize[MAX_FILE_SIZE_SPEC];
  3024. pOpenFileInfo = m_pOpenFileInfo;
  3025. if ( pOpenFileInfo == NULL) {
  3026. return ( ERROR_FILE_NOT_FOUND);
  3027. }
  3028. if ( !pOpenFileInfo->QuerySize(FileSize)) {
  3029. dwError = GetLastError();
  3030. if( dwError != NO_ERROR ) {
  3031. return ( dwError);
  3032. }
  3033. }
  3034. IsLargeIntegerToDecimalChar( &FileSize, rgchSize);
  3035. ReplyToUser( this, REPLY_FILE_STATUS, rgchSize );
  3036. return(dwError);
  3037. }
  3038. APIERR
  3039. USER_DATA::GetFileModTime(LPSYSTEMTIME lpSystemTime)
  3040. {
  3041. DWORD dwError = NO_ERROR;
  3042. TS_OPEN_FILE_INFO * pOpenFileInfo;
  3043. FILETIME FileTime;
  3044. pOpenFileInfo = m_pOpenFileInfo;
  3045. DBG_ASSERT( pOpenFileInfo != NULL );
  3046. if ( !pOpenFileInfo->QueryLastWriteTime(&FileTime)) {
  3047. dwError = GetLastError();
  3048. return ( dwError);
  3049. }
  3050. if (!FileTimeToSystemTime(&FileTime, lpSystemTime)) {
  3051. return GetLastError();
  3052. }
  3053. return NO_ERROR;
  3054. }
  3055. APIERR
  3056. USER_DATA::SendFileToUser( IN LPSTR pszFileName,
  3057. IN OUT LPBOOL pfErrorSent)
  3058. /*++
  3059. This is a worker function for RETR command of FTP. It will establish
  3060. connection via the ( new ) data socket, then send a file over that
  3061. socket. This uses Async io for transmitting the file.
  3062. Arguments:
  3063. pszFileName pointer to null-terminated string containing the filename
  3064. pfErrorSent pointer to boolean flag indicating if an error has
  3065. been already sent to client.
  3066. The flag should be used only when return value is error.
  3067. Returns:
  3068. NO_ERROR on success and Win32 error code if error.
  3069. History:
  3070. 30-April-1995 MuraliK
  3071. --*/
  3072. {
  3073. LARGE_INTEGER FileSize;
  3074. DWORD dwError = NO_ERROR;
  3075. BOOL fTransmit;
  3076. DWORD dwAttribs;
  3077. TS_OPEN_FILE_INFO * pOpenFileInfo;
  3078. CHAR rgchSize[MAX_FILE_SIZE_SPEC];
  3079. CHAR rgchBuffer[MAX_FILE_SIZE_SPEC + 10];
  3080. DBG_ASSERT( pszFileName != NULL && pfErrorSent != NULL);
  3081. *pfErrorSent = FALSE;
  3082. IF_DEBUG( SEND) {
  3083. DBGPRINTF( ( DBG_CONTEXT,
  3084. " USER_DATA ( %08x)::SendFileToUser( %s,"
  3085. " pfErrorSent = %08x).\n",
  3086. this, pszFileName, pfErrorSent));
  3087. }
  3088. //
  3089. // Get file size.
  3090. //
  3091. pOpenFileInfo = m_pOpenFileInfo;
  3092. if ( pOpenFileInfo == NULL) {
  3093. return ( ERROR_FILE_NOT_FOUND);
  3094. }
  3095. // Get the file size
  3096. if ( !pOpenFileInfo->QuerySize(FileSize)) {
  3097. dwError = GetLastError();
  3098. if( dwError != NO_ERROR ) {
  3099. return ( dwError);
  3100. }
  3101. }
  3102. FileSize.QuadPart -= (LONGLONG)QueryCurrentOffset();
  3103. IsLargeIntegerToDecimalChar( &FileSize, rgchSize);
  3104. wsprintfA( rgchBuffer, "(%s bytes)", rgchSize);
  3105. m_pInstance->QueryStatsObj()->IncrTotalFilesSent();
  3106. //
  3107. // Blast the file from a local file to the user.
  3108. //
  3109. Reference(); // incr ref since async data transfer is started
  3110. SET_UF( this, ASYNC_TRANSFER);
  3111. fTransmit = ( m_AioDataConnection.
  3112. TransmitFileTs( pOpenFileInfo,
  3113. FileSize, // cbToSend ( send entire file)
  3114. QueryCurrentOffset() )
  3115. );
  3116. if ( !fTransmit) {
  3117. dwError = GetLastError();
  3118. IF_DEBUG( SEND) {
  3119. DBGPRINTF( ( DBG_CONTEXT,
  3120. " Unable to transmit file ( %s) (pOpenFile = %p)."
  3121. " Error = %u\n",
  3122. pszFileName,
  3123. pOpenFileInfo,
  3124. dwError));
  3125. }
  3126. // decr refcount since async tfr failed.
  3127. DBG_REQUIRE( DeReference() > 0);
  3128. }
  3129. //
  3130. // Disconnect from client.
  3131. // ( will be done at the call back after completion of IO).
  3132. //
  3133. return ( dwError);
  3134. } // USER_DATA::SendFileToUser()
  3135. VOID
  3136. USER_DATA::SetPassiveSocket( IN SOCKET sPassive )
  3137. /*++
  3138. This function frees up an old Passive socket and resets the
  3139. passive socket to the new Passive socket.
  3140. Arguments:
  3141. sPassive - new passive socket to use
  3142. --*/
  3143. {
  3144. SOCKET sPassiveOld;
  3145. sPassiveOld = (SOCKET) InterlockedExchangePointer ( (PVOID *) &m_sPassiveDataListen,
  3146. (PVOID) sPassive);
  3147. if ( sPassiveOld != INVALID_SOCKET) {
  3148. FacDecrement( FacPassiveDataListens);
  3149. DBG_REQUIRE( CloseSocket( sPassiveOld) == 0);
  3150. }
  3151. if ( sPassive != INVALID_SOCKET) {
  3152. FacIncrement(FacPassiveDataListens);
  3153. }
  3154. return;
  3155. } // USER_DATA::SetPassiveSocket()
  3156. VOID
  3157. USER_DATA::CleanupPassiveSocket( BOOL fTellWatchThread )
  3158. /*++
  3159. This function cleans up the resources associated with the current passive socket
  3160. Arguments:
  3161. fTellWatchThread - flag indicating whether to tell thread waiting for an event on
  3162. the current passive socket to clean up as well
  3163. Returns:
  3164. Nothing
  3165. --*/
  3166. {
  3167. SOCKET sPassiveOld;
  3168. LockUser();
  3169. if ( m_sPassiveDataListen == INVALID_SOCKET )
  3170. {
  3171. UnlockUser();
  3172. return;
  3173. }
  3174. RemovePASVAcceptEvent( fTellWatchThread );
  3175. DBG_REQUIRE( CloseSocket( m_sPassiveDataListen ) == 0 );
  3176. m_sPassiveDataListen = INVALID_SOCKET;
  3177. UnlockUser();
  3178. }
  3179. BOOL
  3180. USER_DATA::SetCommand( IN LPSTR pszCmd )
  3181. /*++
  3182. Routine Description:
  3183. Used to set pointer to FTP cmd
  3184. Arguments :
  3185. pszArgs - pointer to command to execute
  3186. Returns :
  3187. BOOL indicating success/failure to set values
  3188. --*/
  3189. {
  3190. BOOL fReturn = TRUE;
  3191. if ( !pszCmd )
  3192. {
  3193. return FALSE;
  3194. }
  3195. //
  3196. // Free any previous allocations
  3197. //
  3198. if ( m_pszCmd )
  3199. {
  3200. TCP_FREE( m_pszCmd );
  3201. m_pszCmd = NULL;
  3202. }
  3203. if ( m_pszCmd = ( LPSTR ) TCP_ALLOC( strlen(pszCmd) + 1 ) )
  3204. {
  3205. strcpy( m_pszCmd, pszCmd );
  3206. }
  3207. else
  3208. {
  3209. DBGPRINTF((DBG_CONTEXT,
  3210. "Failed to allocate memory for command args !\n"));
  3211. fReturn = FALSE;
  3212. }
  3213. return fReturn;
  3214. }
  3215. /************************************************************
  3216. * Auxiliary Functions
  3217. ************************************************************/
  3218. VOID
  3219. ProcessUserAsyncIoCompletion(IN LPVOID pContext,
  3220. IN DWORD cbIo,
  3221. IN DWORD dwError,
  3222. IN LPASYNC_IO_CONNECTION pAioConn,
  3223. IN BOOL fTimedOut
  3224. )
  3225. /*++
  3226. This function processes the Async Io completion ( invoked as
  3227. a callback from the ASYNC_IO_CONNECTION object).
  3228. Arguments:
  3229. pContext pointer to the context information ( UserData object).
  3230. cbIo count of bytes transferred in Io
  3231. dwError DWORD containing the error code resulting from last tfr.
  3232. pAioConn pointer to AsyncIo connection object.
  3233. Returns:
  3234. None
  3235. --*/
  3236. {
  3237. LPUSER_DATA pUserData = (LPUSER_DATA ) pContext;
  3238. DBG_ASSERT( pUserData != NULL);
  3239. DBG_ASSERT( pAioConn != NULL);
  3240. IF_SPECIAL_DEBUG( CRITICAL_PATH) {
  3241. CHAR rgchBuffer[100];
  3242. wsprintfA( rgchBuffer, " ProcessAio( cb=%u, err=%u, Aio=%x). ",
  3243. cbIo, dwError, pAioConn);
  3244. pUserData->Print( rgchBuffer);
  3245. }
  3246. DBG_REQUIRE( pUserData->Reference() > 0);
  3247. # if DBG
  3248. if ( !IS_VALID_USER_DATA( pUserData)) {
  3249. DBGPRINTF( ( DBG_CONTEXT,
  3250. "Encountering an invalid user data ( %08x)\n",
  3251. pUserData));
  3252. pUserData->Print();
  3253. }
  3254. # endif // DBG
  3255. DBG_ASSERT( IS_VALID_USER_DATA( pUserData ) );
  3256. pUserData->ProcessAsyncIoCompletion( cbIo, dwError, pAioConn, fTimedOut);
  3257. DereferenceUserDataAndKill(pUserData);
  3258. return;
  3259. } // ProcessUserAsyncIoCompletion()
  3260. VOID
  3261. USER_DATA::RemovePASVAcceptEvent( BOOL fTellWatchThread )
  3262. /*++
  3263. Routine Description:
  3264. Routine that cleans up the state associated with a PASV accept event
  3265. Arguments:
  3266. fTellWatchThread - BOOL indicating whether or not to inform the thread waiting on
  3267. the event to stop waiting on it
  3268. Returns:
  3269. Nothing
  3270. --*/
  3271. {
  3272. DBG_ASSERT( m_sPassiveDataListen != INVALID_SOCKET );
  3273. if ( m_hPASVAcceptEvent == NULL )
  3274. {
  3275. return;
  3276. }
  3277. //
  3278. // Remove all network notifications for the PASV socket
  3279. //
  3280. if ( WSAEventSelect( m_sPassiveDataListen,
  3281. m_hPASVAcceptEvent,
  3282. 0 ) )
  3283. {
  3284. DBGPRINTF((DBG_CONTEXT,
  3285. "WSAEventSelect on socket %d failed : 0x%x\n",
  3286. m_sPassiveDataListen, WSAGetLastError()));
  3287. }
  3288. //
  3289. // Stop watching for the event
  3290. //
  3291. if ( fTellWatchThread )
  3292. {
  3293. RemoveAcceptEvent( m_hPASVAcceptEvent,
  3294. this );
  3295. }
  3296. WSACloseEvent( m_hPASVAcceptEvent );
  3297. m_hPASVAcceptEvent = NULL;
  3298. }
  3299. SOCKERR
  3300. USER_DATA::AddPASVAcceptEvent( BOOL *pfAcceptableSocket )
  3301. /*++
  3302. Routine Description:
  3303. Routine that sets up the event to signal that the PASV socket is an accept()'able
  3304. state
  3305. Arguments:
  3306. pfAcceptableSocket - BOOL set to TRUE if socket can be accept()'ed at once, FALSE if
  3307. NOT
  3308. Returns:
  3309. Error code indicating success/failure
  3310. --*/
  3311. {
  3312. DWORD dwRet = 0;
  3313. SOCKERR serr = 0;
  3314. BOOL fRegistered = FALSE;
  3315. *pfAcceptableSocket = FALSE;
  3316. if ( ( m_hPASVAcceptEvent = WSACreateEvent() ) == WSA_INVALID_EVENT )
  3317. {
  3318. DBGPRINTF((DBG_CONTEXT,
  3319. "Failed to create event to wait for accept() : 0x%x\n",
  3320. WSAGetLastError()));
  3321. return WSAGetLastError();
  3322. }
  3323. //
  3324. // specify that we want to be alerted when the socket is accept()'able =)
  3325. //
  3326. if ( WSAEventSelect( m_sPassiveDataListen,
  3327. m_hPASVAcceptEvent,
  3328. FD_ACCEPT ) )
  3329. {
  3330. DBGPRINTF((DBG_CONTEXT,
  3331. "WSAEventSelect failed : 0x%x\n",
  3332. WSAGetLastError()));
  3333. serr = WSAGetLastError();
  3334. goto exit;
  3335. }
  3336. else
  3337. {
  3338. fRegistered = TRUE;
  3339. }
  3340. //
  3341. // In order to deal as quickly as possible with legitimate clients and avoid rejecting them
  3342. // because the queue is full, we'll wait for 0.1 sec to see whether the socket becomes
  3343. // accept()'able before queueing it
  3344. //
  3345. dwRet = WSAWaitForMultipleEvents( 1,
  3346. &m_hPASVAcceptEvent,
  3347. FALSE,
  3348. 100,
  3349. FALSE );
  3350. switch ( dwRet )
  3351. {
  3352. case WSA_WAIT_EVENT_0:
  3353. {
  3354. //
  3355. // we can call accept() at once on the socket, no need to muck around with waiting
  3356. // for it
  3357. //
  3358. WSAEventSelect( m_sPassiveDataListen,
  3359. m_hPASVAcceptEvent,
  3360. 0 );
  3361. WSACloseEvent( m_hPASVAcceptEvent );
  3362. m_hPASVAcceptEvent = 0;
  3363. *pfAcceptableSocket = TRUE;
  3364. }
  3365. break;
  3366. case WSA_WAIT_TIMEOUT:
  3367. {
  3368. //
  3369. // Need to queue the socket
  3370. //
  3371. serr = AddAcceptEvent( m_hPASVAcceptEvent,
  3372. this );
  3373. }
  3374. break;
  3375. default:
  3376. {
  3377. serr = WSAGetLastError();
  3378. }
  3379. break;
  3380. }
  3381. exit:
  3382. //
  3383. // clean up if something failed
  3384. //
  3385. if ( serr != 0 )
  3386. {
  3387. if ( m_hPASVAcceptEvent )
  3388. {
  3389. if ( fRegistered )
  3390. {
  3391. WSAEventSelect( m_sPassiveDataListen,
  3392. m_hPASVAcceptEvent,
  3393. 0 );
  3394. }
  3395. WSACloseEvent( m_hPASVAcceptEvent );
  3396. m_hPASVAcceptEvent = NULL;
  3397. }
  3398. }
  3399. return serr;
  3400. }
  3401. VOID
  3402. DereferenceUserDataAndKill(IN OUT LPUSER_DATA pUserData)
  3403. /*++
  3404. This function dereferences User data and kills the UserData object if the
  3405. reference count hits 0. Before killing the user data, it also removes
  3406. the connection from the list of active connections.
  3407. --*/
  3408. {
  3409. FTP_SERVER_INSTANCE * pinstance;
  3410. IF_SPECIAL_DEBUG( CRITICAL_PATH) {
  3411. pUserData->Print( " Deref ");
  3412. }
  3413. //
  3414. // We must capture the instance pointer from the user data, as
  3415. // USER_DATA::RemoveConnection() will set the pointer to NULL.
  3416. // We must also reference the instance before locking it, as
  3417. // removing the last user from the instance will cause the instance
  3418. // to be destroyed. We'll defer this destruction until we're done
  3419. // with the instance.
  3420. //
  3421. pinstance = pUserData->QueryInstance();
  3422. pinstance->Reference();
  3423. pinstance->LockConnectionsList();
  3424. if ( !pUserData->DeReference()) {
  3425. //
  3426. // Deletion of the object USER_DATA is required.
  3427. //
  3428. IF_DEBUG( USER_DATABASE) {
  3429. DBGPRINTF( ( DBG_CONTEXT,
  3430. " UserData( %08x) is being deleted.\n",
  3431. pUserData));
  3432. }
  3433. pinstance->UnlockConnectionsList();
  3434. pUserData->Cleanup();
  3435. DBG_ASSERT( pUserData->QueryControlSocket() == INVALID_SOCKET );
  3436. DBG_ASSERT( pUserData->QueryDataSocket() == INVALID_SOCKET );
  3437. pinstance->RemoveConnection( pUserData);
  3438. }
  3439. else {
  3440. pinstance->UnlockConnectionsList();
  3441. }
  3442. pinstance->Dereference();
  3443. } // DereferenceUserDataAndKill()
  3444. BOOL
  3445. PathAccessCheck(IN ACCESS_TYPE _access,
  3446. IN DWORD dwVrootAccessMask,
  3447. IN BOOL fUserRead,
  3448. IN BOOL fUserWrite
  3449. )
  3450. /*++
  3451. This function determines if the required privilege to access the specified
  3452. virtual root with a given access mask exists.
  3453. Arguments:
  3454. access - specifies type of acces desired.
  3455. dwVrootAccessMask - DWORD containing the access mask for the virtual root.
  3456. fUserRead - user's permission to read (general)
  3457. fUserWrite - user's permission to write (general)
  3458. Returns:
  3459. BOOL - TRUE if access is to be granted, else FALSE.
  3460. History:
  3461. MuraliK 20-Sept-1995
  3462. --*/
  3463. {
  3464. BOOL fAccessGranted = FALSE;
  3465. DBG_ASSERT( IS_VALID_ACCESS_TYPE( _access ) );
  3466. //
  3467. // Perform the actual access check.
  3468. //
  3469. switch( _access ) {
  3470. case AccessTypeRead :
  3471. fAccessGranted = (fUserRead &&
  3472. ((dwVrootAccessMask & VROOT_MASK_READ)
  3473. == VROOT_MASK_READ)
  3474. );
  3475. break;
  3476. case AccessTypeWrite :
  3477. case AccessTypeCreate :
  3478. case AccessTypeDelete :
  3479. fAccessGranted = (fUserWrite &&
  3480. ((dwVrootAccessMask & VROOT_MASK_WRITE)
  3481. == VROOT_MASK_WRITE)
  3482. );
  3483. break;
  3484. default :
  3485. DBGPRINTF(( DBG_CONTEXT,
  3486. "PathAccessCheck - invalid access type %d\n",
  3487. _access ));
  3488. DBG_ASSERT( FALSE );
  3489. break;
  3490. }
  3491. if (!fAccessGranted) {
  3492. SetLastError( ERROR_ACCESS_DENIED);
  3493. }
  3494. return ( fAccessGranted);
  3495. } // PathAccessCheck()
  3496. VOID SignalAcceptableSocket( LPUSER_DATA pUserData )
  3497. /*++
  3498. Function that restarts processing the original command when a PASV data socket becomes
  3499. accept()'able [ie the client has made the connection]
  3500. Arguments:
  3501. pUserData - USER_DATA context attached to socket
  3502. Returns:
  3503. Nothing
  3504. --*/
  3505. {
  3506. PATQ_CONTEXT pAtqContext = pUserData->QueryControlAio()->QueryAtqContext();
  3507. //
  3508. // Stop waiting for events on this socket
  3509. //
  3510. pUserData->RemovePASVAcceptEvent( FALSE );
  3511. pUserData->SetInFakeIOCompletion( TRUE );
  3512. //
  3513. // do a scary thing - fake an IO completion, to trigger re-processing of the FTP command
  3514. //
  3515. if ( !AtqPostCompletionStatus( pAtqContext,
  3516. strlen( pUserData->QueryCmdString() ) + 1 ) )
  3517. {
  3518. DBGPRINTF((DBG_CONTEXT,
  3519. "Failed to post fake completion status to deal with PASV event : 0x%x\n",
  3520. GetLastError()));
  3521. return;
  3522. }
  3523. }
  3524. VOID CleanupTimedOutSocketContext( LPUSER_DATA pUserData )
  3525. /*++
  3526. Function used to do cleanup when timeout for waiting for a PASV connection expires
  3527. Arguments:
  3528. pUserData - context pointer
  3529. Returns:
  3530. Nothing
  3531. --*/
  3532. {
  3533. DBG_ASSERT( pUserData );
  3534. pUserData->LockUser();
  3535. pUserData->CleanupPassiveSocket( FALSE );
  3536. CLEAR_UF( pUserData, PASSIVE );
  3537. pUserData->SetHavePASVConn( FALSE );
  3538. pUserData->SetWaitingForPASVConn( FALSE );
  3539. ReplyToUser( pUserData,
  3540. REPLY_CANNOT_OPEN_CONNECTION,
  3541. PSZ_CANNOT_OPEN_DATA_CONNECTION );
  3542. //
  3543. // Remove our reference to this USER_DATA object
  3544. //
  3545. pUserData->DeReference();
  3546. pUserData->UnlockUser();
  3547. }
  3548. /*******************************************************************
  3549. NAME: FtpMetaDataFree
  3550. SYNOPSIS: Frees a formatted meta data object when it's not in use.
  3551. ENTRY: pObject - Pointer to the meta data object.
  3552. RETURNS:
  3553. NOTES:
  3554. ********************************************************************/
  3555. VOID
  3556. FtpMetaDataFree(
  3557. PVOID pObject
  3558. )
  3559. {
  3560. PFTP_METADATA pMD;
  3561. pMD = (PFTP_METADATA)pObject;
  3562. delete pMD;
  3563. }
  3564. BOOL
  3565. FTP_METADATA::HandlePrivateProperty(
  3566. LPSTR pszURL,
  3567. PIIS_SERVER_INSTANCE pInstance,
  3568. METADATA_GETALL_INTERNAL_RECORD *pMDRecord,
  3569. LPVOID pDataPointer,
  3570. BUFFER *pBuffer,
  3571. DWORD *pdwBytesUsed,
  3572. PMETADATA_ERROR_INFO pMDErrorInfo
  3573. )
  3574. /*++
  3575. Routine Description:
  3576. Handle metabase properties private to FTP service
  3577. Arguments:
  3578. pszURL - URL of the requested object
  3579. pInstance - FTP server instance
  3580. pMDRecord - metadata record
  3581. pDataPointer - pointer to metabase data
  3582. pBuffer - Buffer available for storage space
  3583. pdwBytesUsed - Pointer to bytes used in *pBuffer
  3584. Returns:
  3585. BOOL - TRUE success ( or not handled ), otherwise FALSE.
  3586. --*/
  3587. {
  3588. return TRUE;
  3589. }
  3590. BOOL
  3591. FTP_METADATA::FinishPrivateProperties(
  3592. BUFFER *pBuffer,
  3593. DWORD dwBytesUsed,
  3594. BOOL bSucceeded
  3595. )
  3596. /*++
  3597. Routine Description:
  3598. Handles completion of reading metabase properties private to FTP.
  3599. Arguments:
  3600. pBuffer - Buffer previously used for storage space
  3601. dwBytesUsed - bytes used in *pBuffer
  3602. Returns:
  3603. BOOL - TRUE success ( or not handled ), otherwise FALSE.
  3604. --*/
  3605. {
  3606. return TRUE;
  3607. }
  3608. BOOL
  3609. USER_DATA::LookupVirtualRoot(
  3610. IN const CHAR * pszURL,
  3611. OUT CHAR * pszPath,
  3612. OUT DWORD * pcchDirRoot,
  3613. OUT DWORD * pdwAccessMask
  3614. )
  3615. /*++
  3616. Routine Description:
  3617. Looks up the virtual root to find the physical drive mapping. If an
  3618. Accept-Language header was sent by the client, we look for a virtual
  3619. root prefixed by the language tag
  3620. Arguments:
  3621. pstrPath - Receives physical drive path
  3622. pszURL - URL to look for
  3623. pcchDirRoot - Number of characters in the found physical path
  3624. pdwMask - Access mask for the specified URL
  3625. Returns:
  3626. BOOL - TRUE if success, otherwise FALSE.
  3627. --*/
  3628. {
  3629. PFTP_METADATA pMD;
  3630. DWORD dwDataSetNumber;
  3631. PVOID pCacheInfo;
  3632. MB mb( (IMDCOM*) g_pInetSvc->QueryMDObject() );
  3633. STACK_STR( strFullPath, MAX_PATH );
  3634. METADATA_ERROR_INFO MDErrorInfo;
  3635. BOOL fOk;
  3636. DWORD dwError = NO_ERROR;
  3637. if ( m_pMetaData != NULL )
  3638. {
  3639. TsFreeMetaData( m_pMetaData->QueryCacheInfo() );
  3640. m_pMetaData = NULL;
  3641. }
  3642. // First read the data set number, and see if we already have it
  3643. // cached. We don't do a full open in this case.
  3644. if ( !strFullPath.Copy( m_pInstance->QueryMDVRPath() ) ||
  3645. !strFullPath.Append( ( *pszURL == '/' ) ? pszURL + 1 : pszURL ) )
  3646. {
  3647. goto LookupVirtualRoot_Error;
  3648. }
  3649. if (!mb.GetDataSetNumber( strFullPath.QueryStr(),
  3650. &dwDataSetNumber ))
  3651. {
  3652. goto LookupVirtualRoot_Error;
  3653. }
  3654. // See if we can find a matching data set already formatted.
  3655. pMD = (PFTP_METADATA)TsFindMetaData(dwDataSetNumber, METACACHE_FTP_SERVER_ID);
  3656. if (pMD == NULL)
  3657. {
  3658. pMD = new FTP_METADATA;
  3659. if (pMD == NULL)
  3660. {
  3661. goto LookupVirtualRoot_Error;
  3662. }
  3663. if ( !pMD->ReadMetaData( m_pInstance,
  3664. &mb,
  3665. (LPSTR)pszURL,
  3666. &MDErrorInfo ) )
  3667. {
  3668. delete pMD;
  3669. goto LookupVirtualRoot_Error;
  3670. }
  3671. // We were succesfull, so try and add this metadata. There is a race
  3672. // condition where someone else could have added it while we were
  3673. // formatting. This is OK - we'll have two cached, but they should be
  3674. // consistent, and one of them will eventually time out. We could have
  3675. // AddMetaData check for this, and free the new one while returning a
  3676. // pointer to the old one if it finds one, but that isn't worthwhile
  3677. // now.
  3678. pCacheInfo = TsAddMetaData(pMD, FtpMetaDataFree,
  3679. dwDataSetNumber, METACACHE_FTP_SERVER_ID);
  3680. }
  3681. m_pMetaData = pMD;
  3682. if ( m_pMetaData->QueryVrError() )
  3683. {
  3684. dwError = m_pMetaData->QueryVrError();
  3685. goto LookupVirtualRoot_Error;
  3686. }
  3687. //
  3688. // Build physical path from VR_PATH & portion of URI not used to define VR_PATH
  3689. //
  3690. if (m_pInstance->QueryIsolationMode() == NoIsolation)
  3691. {
  3692. fOk = pMD->BuildPhysicalPath( (LPSTR)pszURL, &strFullPath );
  3693. }
  3694. else
  3695. {
  3696. fOk = pMD->BuildPhysicalPathWithAltRoot( (LPSTR)pszURL, &strFullPath, m_szRootDir );
  3697. }
  3698. if ( fOk &&
  3699. *pcchDirRoot > strFullPath.QueryCCH() )
  3700. {
  3701. memcpy( pszPath, strFullPath.QueryStr(), strFullPath.QueryCCH()+1 );
  3702. *pcchDirRoot = strFullPath.QueryCCH();
  3703. if ( pdwAccessMask )
  3704. {
  3705. *pdwAccessMask = m_pMetaData->QueryAccessPerms();
  3706. }
  3707. return TRUE;
  3708. }
  3709. LookupVirtualRoot_Error:
  3710. if (dwError == NO_ERROR) {
  3711. //
  3712. // best error message to send to client
  3713. //
  3714. dwError = ERROR_FILE_NOT_FOUND;
  3715. }
  3716. SetLastError( dwError );
  3717. return FALSE;
  3718. }
  3719. BOOL
  3720. USER_DATA::BindInstanceAccessCheck(
  3721. )
  3722. /*++
  3723. Routine Description:
  3724. Bind IP/DNS access check for this request to instance data
  3725. Arguments:
  3726. None
  3727. Returns:
  3728. BOOL - TRUE if success, otherwise FALSE.
  3729. --*/
  3730. {
  3731. if ( m_rfAccessCheck.CopyFrom( m_pInstance->QueryMetaDataRefHandler() ) )
  3732. {
  3733. m_acAccessCheck.BindCheckList( (LPBYTE)m_rfAccessCheck.GetPtr(), m_rfAccessCheck.GetSize() );
  3734. return TRUE;
  3735. }
  3736. return FALSE;
  3737. }
  3738. VOID
  3739. USER_DATA::UnbindInstanceAccessCheck()
  3740. /*++
  3741. Routine Description:
  3742. Unbind IP/DNS access check for this request to instance data
  3743. Arguments:
  3744. None
  3745. Returns:
  3746. Nothing
  3747. --*/
  3748. {
  3749. m_acAccessCheck.UnbindCheckList();
  3750. m_rfAccessCheck.Reset( (IMDCOM*) g_pInetSvc->QueryMDObject() );
  3751. }
  3752. BOOL
  3753. USER_DATA::IsFileNameShort( IN LPSTR pszFile)
  3754. /*++
  3755. Check file name beeing short or not.
  3756. Arguments:
  3757. pszFile pointer to null-terminated string containing the file name
  3758. Returns:
  3759. TRUE if filename is short.
  3760. --*/
  3761. {
  3762. APIERR err;
  3763. CHAR szCanonPath[MAX_PATH];
  3764. DWORD cbSize = MAX_PATH*sizeof(CHAR);
  3765. CHAR szVirtualPath[MAX_PATH+1];
  3766. DWORD cchVirtualPath = MAX_PATH;
  3767. BOOL fShort;
  3768. BOOL fRet = FALSE;
  3769. DBG_ASSERT( pszFile != NULL );
  3770. //
  3771. // Close any file we might have open now
  3772. // N.B. There shouldn't be an open file; we're just
  3773. // being careful here.
  3774. //
  3775. if (m_pOpenFileInfo) {
  3776. DBGPRINTF(( DBG_CONTEXT,
  3777. "WARNING!! Closing [%08x], before opening %s\n",
  3778. pszFile
  3779. ));
  3780. DBG_REQUIRE( CloseFileForSend() );
  3781. }
  3782. //
  3783. // Open the requested file
  3784. //
  3785. err = VirtualCanonicalize(szCanonPath,
  3786. &cbSize,
  3787. pszFile,
  3788. AccessTypeRead,
  3789. NULL,
  3790. szVirtualPath,
  3791. &cchVirtualPath);
  3792. if( err == NO_ERROR )
  3793. {
  3794. if ( strchr( szCanonPath, '~' ))
  3795. {
  3796. err = CheckIfShortFileName( (UCHAR *) szCanonPath, TsTokenToImpHandle( QueryUserToken()), &fShort );
  3797. if ( !err && fShort)
  3798. {
  3799. DBGPRINTF(( DBG_CONTEXT,
  3800. "Short filename being rejected \"%s\"\n",
  3801. szCanonPath ));
  3802. fRet = TRUE;
  3803. }
  3804. }
  3805. }
  3806. return fRet;
  3807. } // USER_DATA::IsFileNameShort()
  3808. DWORD
  3809. USER_DATA::CheckIfShortFileName(
  3810. IN CONST UCHAR * pszPath,
  3811. IN HANDLE hImpersonation,
  3812. OUT BOOL * pfShort
  3813. )
  3814. /*++
  3815. Description:
  3816. This function takes a suspected NT/Win95 short filename and checks if there's
  3817. an equivalent long filename. For example, c:\foobar\ABCDEF~1.ABC is the same
  3818. as c:\foobar\abcdefghijklmnop.abc.
  3819. NOTE: This function should be called unimpersonated - the FindFirstFile() must
  3820. be called in the system context since most systems have traverse checking turned
  3821. off - except for the UNC case where we must be impersonated to get network access.
  3822. Arguments:
  3823. pszPath - Path to check
  3824. hImpersonation - Impersonation handle if this is a UNC path - can be NULL if not UNC
  3825. pfShort - Set to TRUE if an equivalent long filename is found
  3826. Returns:
  3827. Win32 error on failure
  3828. --*/
  3829. {
  3830. DWORD err = NO_ERROR;
  3831. WIN32_FIND_DATA FindData;
  3832. UCHAR * psz;
  3833. BOOL fUNC;
  3834. psz = _mbschr( (UCHAR *) pszPath, '~' );
  3835. *pfShort = FALSE;
  3836. fUNC = (*pszPath == '\\');
  3837. //
  3838. // Loop for multiple tildas - watch for a # after the tilda
  3839. //
  3840. while ( psz++ )
  3841. {
  3842. if ( *psz >= '0' && *psz <= '9' )
  3843. {
  3844. UCHAR achTmp[MAX_PATH];
  3845. UCHAR * pchEndSeg;
  3846. UCHAR * pchBeginSeg;
  3847. HANDLE hFind;
  3848. //
  3849. // Isolate the path up to the segment with the
  3850. // '~' and do the FindFirst with that path
  3851. //
  3852. pchEndSeg = _mbschr( psz, '\\' );
  3853. if ( !pchEndSeg )
  3854. {
  3855. pchEndSeg = psz + _mbslen( psz );
  3856. }
  3857. //
  3858. // If the string is beyond MAX_PATH then we allow it through
  3859. //
  3860. if ( ((INT) (pchEndSeg - pszPath)) >= sizeof( achTmp ))
  3861. {
  3862. return NO_ERROR;
  3863. }
  3864. memcpy( achTmp, pszPath, (INT) (pchEndSeg - pszPath) );
  3865. achTmp[pchEndSeg - pszPath] = '\0';
  3866. if ( fUNC && hImpersonation )
  3867. {
  3868. if ( !ImpersonateLoggedOnUser( hImpersonation ))
  3869. {
  3870. return GetLastError();
  3871. }
  3872. }
  3873. hFind = FindFirstFile( (CHAR *) achTmp, &FindData );
  3874. if ( fUNC && hImpersonation )
  3875. {
  3876. RevertToSelf();
  3877. }
  3878. if ( hFind == INVALID_HANDLE_VALUE )
  3879. {
  3880. err = GetLastError();
  3881. DBGPRINTF(( DBG_CONTEXT,
  3882. "FindFirst failed!! - \"%s\", error %d\n",
  3883. achTmp,
  3884. GetLastError() ));
  3885. //
  3886. // If the FindFirstFile() fails to find the file then return
  3887. // success - the path doesn't appear to be a valid path which
  3888. // is ok.
  3889. //
  3890. if ( err == ERROR_FILE_NOT_FOUND ||
  3891. err == ERROR_PATH_NOT_FOUND )
  3892. {
  3893. return NO_ERROR;
  3894. }
  3895. return err;
  3896. }
  3897. DBG_REQUIRE( FindClose( hFind ));
  3898. //
  3899. // Isolate the last segment of the string which should be
  3900. // the potential short name equivalency
  3901. //
  3902. pchBeginSeg = _mbsrchr( achTmp, '\\' );
  3903. DBG_ASSERT( pchBeginSeg );
  3904. pchBeginSeg++;
  3905. //
  3906. // If the last segment doesn't match the long name then this is
  3907. // the short name version of the path
  3908. //
  3909. if ( _mbsicmp( (UCHAR *) FindData.cFileName, pchBeginSeg ))
  3910. {
  3911. *pfShort = TRUE;
  3912. return NO_ERROR;
  3913. }
  3914. }
  3915. psz = _mbschr( psz, '~' );
  3916. }
  3917. return err;
  3918. }
  3919. /******************************* End Of File *************************/