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.

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