Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2004 lines
58 KiB

  1. /*++
  2. Copyright (c) 1994 Microsoft Corporation
  3. Module Name:
  4. protocol.cxx
  5. Abstract:
  6. Contains functions to negotiate data connections with, send commands to and
  7. receive data from, the FTP server
  8. Contents:
  9. Command
  10. I_Command
  11. NegotiateDataConnection
  12. GetReply
  13. ReceiveFtpResponse
  14. AbortTransfer
  15. (SendCommand)
  16. (I_SendCommand)
  17. (I_AttemptDataNegotiation)
  18. (I_NegotiateDataConnection)
  19. Author:
  20. Heath Hunnicutt (t-hheath) 21-Jun-1994
  21. Environment:
  22. Win32 user-level DLL
  23. Revision History:
  24. --*/
  25. #include <wininetp.h>
  26. #include "ftpapih.h"
  27. //
  28. // private prototypes
  29. //
  30. PRIVATE
  31. DWORD
  32. __cdecl
  33. SendCommand(
  34. IN ICSocket * s,
  35. IN LPCSTR lpszFormat,
  36. IN ...
  37. );
  38. PRIVATE
  39. DWORD
  40. I_SendCommand(
  41. IN ICSocket * s,
  42. IN LPCSTR lpszFormat,
  43. va_list arglist
  44. );
  45. PRIVATE
  46. DWORD
  47. I_AttemptDataNegotiation(
  48. IN LPFTP_SESSION_INFO lpSessionInfo,
  49. IN DWORD dwFlags,
  50. IN OUT FTP_RESPONSE_CODE *prcResponse,
  51. IN LPCSTR lpszCommandFormat,
  52. IN va_list arglist
  53. );
  54. PRIVATE
  55. DWORD
  56. I_NegotiateDataConnection(
  57. IN LPFTP_SESSION_INFO lpSessionInfo,
  58. IN DWORD dwFlags,
  59. IN OUT FTP_RESPONSE_CODE *prcResponse,
  60. IN LPCSTR lpszCommandFormat,
  61. IN va_list arglist,
  62. IN BOOL bOverridePassive
  63. );
  64. //
  65. // functions
  66. //
  67. DWORD
  68. Command(
  69. IN LPFTP_SESSION_INFO lpSessionInfo,
  70. IN BOOL fExpectResponse,
  71. IN DWORD dwFlags,
  72. IN OUT FTP_RESPONSE_CODE *prcResponse,
  73. IN LPCSTR lpszCommandFormat,
  74. IN ...
  75. )
  76. /*++
  77. Routine Description:
  78. Sends a command to the FTP server on the control connection and optionally
  79. sets up a data connection. Wrapper for I_Command()
  80. Arguments:
  81. lpSessionInfo - pointer to FTP_SESSION_INFO describing FTP server and
  82. our connection to it
  83. fExpectResponse - TRUE if we need a data connection
  84. dwFlags - controlling how to transfer data between the client
  85. and server data connection, and how to set up data
  86. connection
  87. prcResponse - pointer to response code returned from server
  88. lpszCommandFormat - pointer to command string
  89. ... - optional arguments for command string
  90. Return Value:
  91. DWORD
  92. Success - ERROR_SUCCESS
  93. Failure - WSA error
  94. --*/
  95. {
  96. DEBUG_ENTER((DBG_FTP,
  97. Dword,
  98. "Command",
  99. "%#x, %B, %#x, %#x, %q",
  100. lpSessionInfo,
  101. fExpectResponse,
  102. dwFlags,
  103. prcResponse,
  104. lpszCommandFormat
  105. ));
  106. DWORD error;
  107. va_list arglist;
  108. va_start(arglist, lpszCommandFormat);
  109. error = I_Command(lpSessionInfo,
  110. fExpectResponse,
  111. dwFlags,
  112. prcResponse,
  113. lpszCommandFormat,
  114. arglist
  115. );
  116. va_end(arglist);
  117. DEBUG_LEAVE(error);
  118. return error;
  119. }
  120. DWORD
  121. I_Command(
  122. IN LPFTP_SESSION_INFO lpSessionInfo,
  123. IN BOOL fExpectResponse,
  124. IN DWORD dwFlags,
  125. IN OUT FTP_RESPONSE_CODE *prcResponse,
  126. IN LPCSTR lpszCommandFormat,
  127. IN va_list arglist
  128. )
  129. /*++
  130. Routine Description:
  131. Sends a command to the FTP server, and if requested, negotiates a data
  132. connection
  133. Arguments:
  134. lpSessionInfo - pointer to FTP_SESSION_INFO describing the FTP server
  135. and our connection to it
  136. fExpectResponse - TRUE if we need a data connection
  137. dwFlags - controlling how to transfer data between the client
  138. and server data connection, and how to set up data
  139. connection
  140. prcResponse - pointer to returned server response
  141. lpszCommandFormat - command to send to server
  142. arglist - optional arguments for lpszCommandFormat
  143. Return Value:
  144. DWORD
  145. Success - ERROR_SUCCESS
  146. Failure - WSA error
  147. --*/
  148. {
  149. DEBUG_ENTER((DBG_FTP,
  150. Dword,
  151. "I_Command",
  152. "%#x, %B, %#x, %#x, %q, %#x",
  153. lpSessionInfo,
  154. fExpectResponse,
  155. dwFlags,
  156. prcResponse,
  157. lpszCommandFormat,
  158. arglist
  159. ));
  160. DWORD error;
  161. INET_ASSERT(lpSessionInfo != NULL);
  162. INET_ASSERT(prcResponse != NULL);
  163. INET_ASSERT(lpszCommandFormat != NULL);
  164. INET_ASSERT( ((dwFlags & FTP_TRANSFER_TYPE_MASK) == FTP_TRANSFER_TYPE_UNKNOWN)
  165. || ((dwFlags & FTP_TRANSFER_TYPE_MASK) == FTP_TRANSFER_TYPE_ASCII)
  166. || ((dwFlags & FTP_TRANSFER_TYPE_MASK) == FTP_TRANSFER_TYPE_BINARY)
  167. );
  168. //
  169. // if the control socket is not valid, return "connection dropped"
  170. //
  171. if (!lpSessionInfo->socketControl->IsValid()) {
  172. error = ERROR_FTP_DROPPED;
  173. goto quit;
  174. }
  175. //
  176. // there can only be one data connection established for each FTP session
  177. //
  178. if (lpSessionInfo->socketData->IsValid()) {
  179. error = ERROR_FTP_TRANSFER_IN_PROGRESS;
  180. goto quit;
  181. }
  182. //
  183. // if we need a data connection then send the connection set-up commands
  184. // and issue the command
  185. //
  186. if (fExpectResponse) {
  187. error = I_AttemptDataNegotiation(lpSessionInfo,
  188. dwFlags,
  189. prcResponse,
  190. lpszCommandFormat,
  191. arglist
  192. );
  193. if (error == ERROR_SUCCESS) {
  194. //
  195. // check the server response for failure
  196. //
  197. if ((prcResponse->Major != FTP_RESPONSE_PRELIMINARY)
  198. && (prcResponse->Major != FTP_RESPONSE_COMPLETE)) {
  199. error = ERROR_INTERNET_EXTENDED_ERROR;
  200. }
  201. }
  202. } else {
  203. //
  204. // no data connection required, just send the command and check any
  205. // response from the server for a failure indication
  206. //
  207. error = I_SendCommand(lpSessionInfo->socketControl,
  208. lpszCommandFormat,
  209. arglist
  210. );
  211. if (error == ERROR_SUCCESS) {
  212. error = GetReply(lpSessionInfo, prcResponse);
  213. if (error == ERROR_SUCCESS) {
  214. if ((prcResponse->Major != FTP_RESPONSE_COMPLETE)
  215. && (prcResponse->Major != FTP_RESPONSE_CONTINUE)
  216. && (prcResponse->Major != FTP_RESPONSE_PRELIMINARY)) {
  217. error = ERROR_INTERNET_EXTENDED_ERROR;
  218. }
  219. }
  220. }
  221. }
  222. quit:
  223. DEBUG_LEAVE(error);
  224. return error;
  225. }
  226. DWORD
  227. NegotiateDataConnection(
  228. IN LPFTP_SESSION_INFO lpSessionInfo,
  229. IN DWORD dwFlags,
  230. IN OUT FTP_RESPONSE_CODE *prcResponse,
  231. IN LPCSTR lpszCommandFormat,
  232. IN ...
  233. )
  234. /*++
  235. Routine Description:
  236. Sets up a data connection between this client and the FTP server. Wrapper
  237. for I_AttemptDataNegotiation()
  238. Arguments:
  239. lpSessionInfo - pointer to FTP_SESSION_INFO describing the FTP server
  240. and our connection to it
  241. dwFlags - controlling how to transfer data between the client
  242. and server data connection, and how to set up data
  243. connection
  244. prcResponse - pointer to returned server response code
  245. lpszCommandFormat - command string to send
  246. ... - optional arguments for lpszCommandFormat
  247. Return Value:
  248. DWORD
  249. Success - ERROR_SUCCESS
  250. Failure - WSA error
  251. error returned by Windows sockets
  252. --*/
  253. {
  254. DEBUG_ENTER((DBG_FTP,
  255. Dword,
  256. "NegotiateDataConnection",
  257. "%#x, %#x, %#x, %q",
  258. lpSessionInfo,
  259. dwFlags,
  260. prcResponse,
  261. lpszCommandFormat
  262. ));
  263. va_list arglist;
  264. va_start(arglist, lpszCommandFormat);
  265. DWORD error = I_AttemptDataNegotiation(lpSessionInfo,
  266. dwFlags,
  267. prcResponse,
  268. lpszCommandFormat,
  269. arglist
  270. );
  271. va_end(arglist);
  272. DEBUG_LEAVE(error);
  273. return error;
  274. }
  275. DWORD
  276. GetReply(
  277. IN LPFTP_SESSION_INFO lpSessionInfo,
  278. OUT FTP_RESPONSE_CODE *prcResponse
  279. )
  280. /*++
  281. Routine Description:
  282. Gets the response code from the server. The response text is stored in the
  283. per-thread last response text field, and the FTP response code is parsed
  284. off the start of the text and returned in prcResponse
  285. Arguments:
  286. lpSessionInfo - pointer to FTP_SESSION_INFO for which to get response text
  287. prcResponse - pointer to the response code structure
  288. Return Value:
  289. DWORD
  290. Success - ERROR_SUCCESS
  291. Failure - Win32 error
  292. --*/
  293. {
  294. DEBUG_ENTER((DBG_FTP,
  295. Dword,
  296. "GetReply",
  297. "%#x, %#x",
  298. lpSessionInfo,
  299. prcResponse
  300. ));
  301. PCHAR pchReplyBuffer;
  302. DWORD cchBufferLength;
  303. DWORD error;
  304. INET_ASSERT(prcResponse != NULL);
  305. //
  306. // set up a default error code
  307. //
  308. prcResponse->Major = FTP_RESPONSE_PERMANENT_FAILURE;
  309. prcResponse->Minor = 0;
  310. prcResponse->Detail = 0;
  311. prcResponse->Status = 0;
  312. pchReplyBuffer = NULL;
  313. //
  314. // receive the last response on the control socket
  315. //
  316. error = ReceiveFtpResponse(lpSessionInfo->socketControl,
  317. (LPVOID *)&pchReplyBuffer,
  318. &cchBufferLength,
  319. TRUE,
  320. prcResponse
  321. );
  322. if (error == ERROR_SUCCESS) {
  323. INET_ASSERT(pchReplyBuffer != NULL);
  324. //
  325. // append this response text to that already stored in this thread's
  326. // data object
  327. //
  328. InternetSetLastError(0,
  329. pchReplyBuffer,
  330. cchBufferLength,
  331. SLE_APPEND | SLE_ZERO_TERMINATE
  332. );
  333. //
  334. // extract status code
  335. //
  336. LPSTR lpszError = pchReplyBuffer;
  337. ExtractInt(&lpszError, 0, &prcResponse->Status);
  338. DEBUG_PRINT(PROTOCOL, INFO, ("FTP status = %d\n", prcResponse->Status));
  339. } else {
  340. INET_ASSERT(pchReplyBuffer == NULL);
  341. }
  342. //
  343. // finished with the buffer
  344. //
  345. if (pchReplyBuffer != NULL) {
  346. pchReplyBuffer = (PCHAR)FREE_MEMORY((HLOCAL)pchReplyBuffer);
  347. INET_ASSERT(pchReplyBuffer == NULL);
  348. }
  349. DEBUG_LEAVE(error);
  350. return error;
  351. }
  352. DWORD
  353. ReceiveFtpResponse(
  354. IN ICSocket * Socket,
  355. OUT LPVOID * lpBuffer,
  356. OUT LPDWORD lpdwBufferLength,
  357. IN BOOL fEndOfLineCheck,
  358. IN FTP_RESPONSE_CODE * prcResponse
  359. )
  360. /*++
  361. Routine Description:
  362. Receives data from the FTP server. Optionally parses it for end-of-line
  363. sequence
  364. This function is typically going to receive one or more lines of response
  365. data, i.e. a small amount of data. Also typically, we will not normally
  366. expect to get EOF(connection) because we are usually reading the control
  367. connection
  368. Arguments:
  369. Socket - socket on which to receive data
  370. lpBuffer - pointer to pointer to returned data buffer
  371. lpdwBufferLength - pointer to returned length of (returned) data buffer
  372. fEndOfLineCheck - TRUE if we are to perform an end-of-line check
  373. prcResponse - pointer to returned server response code
  374. Return Value:
  375. DWORD
  376. Success - ERROR_SUCCESS
  377. Failure - WSA error
  378. --*/
  379. {
  380. DEBUG_ENTER((DBG_FTP,
  381. Dword,
  382. "ReceiveFtpResponse",
  383. "%#x, %#x, %#x, %B, %#x",
  384. Socket,
  385. lpBuffer,
  386. lpdwBufferLength,
  387. fEndOfLineCheck,
  388. prcResponse
  389. ));
  390. INET_ASSERT(lpBuffer != NULL);
  391. INET_ASSERT(lpdwBufferLength != NULL);
  392. if (fEndOfLineCheck) {
  393. INET_ASSERT(prcResponse != NULL);
  394. }
  395. LPSTR pchBuffer;
  396. DWORD bufferLength;
  397. DWORD bufferLeft;
  398. DWORD bytesReceived;
  399. int idxPosInLine;
  400. BOOL fLastLineDigitsSeen;
  401. BOOL fAtEOL;
  402. int nNumericReply;
  403. DWORD error;
  404. //
  405. // initialize variables (for SocketReceive())
  406. //
  407. pchBuffer = NULL;
  408. bufferLength = 0;
  409. bufferLeft = 0;
  410. bytesReceived = 0;
  411. //
  412. // get per-thread info block to determine blocking/non-blocking socket calls
  413. //
  414. LPINTERNET_THREAD_INFO lpThreadInfo = InternetGetThreadInfo();
  415. if (lpThreadInfo == NULL) {
  416. INET_ASSERT(FALSE);
  417. error = ERROR_INTERNET_INTERNAL_ERROR;
  418. goto done;
  419. }
  420. //DWORD asyncFlags;
  421. //
  422. //asyncFlags = 0;
  423. //asyncFlags = lpThreadInfo->IsAsyncWorkerThread ? SF_NON_BLOCKING : 0;
  424. //
  425. // this flag is set to FALSE once the digits are definitely NOT seen, and
  426. // needs to be TRUE at the start of each new line
  427. //
  428. fLastLineDigitsSeen = TRUE;
  429. nNumericReply = 0;
  430. idxPosInLine = 0;
  431. fAtEOL = FALSE;
  432. BOOL eofData;
  433. while (TRUE) {
  434. //
  435. // get the next chunk of response data from the server. Since we are
  436. // expecting a response (i.e. small amount of data (< 1K)) we shouldn't
  437. // have to do this too many times
  438. //
  439. error = Socket->Receive(
  440. (LPVOID *)&pchBuffer,
  441. &bufferLength,
  442. &bufferLeft,
  443. &bytesReceived,
  444. sizeof('\0'), // dwExtraSpace
  445. SF_EXPAND // SocketReceive will allocate/grow the buffer
  446. | SF_COMPRESS // and compress any unused space at EOF
  447. | SF_INDICATE, // and make indications to app
  448. &eofData
  449. );
  450. if (error != ERROR_SUCCESS) {
  451. goto quit;
  452. }
  453. //
  454. // if we have hit the end of the data then zero-terminate the buffer
  455. //
  456. if (eofData) {
  457. INET_ASSERT(bufferLeft > 0);
  458. pchBuffer[bytesReceived] = '\0';
  459. }
  460. //
  461. // if requested, find out if we got the end-of-line (on the last line)
  462. //
  463. if (fEndOfLineCheck) {
  464. //
  465. // look for the final line of the response
  466. //
  467. for (DWORD bytesChecked = 0;
  468. bytesChecked < bytesReceived;
  469. ++bytesChecked, ++idxPosInLine) {
  470. //
  471. // ISSUE: fAtEOL && fLastLineDigitsSeen ==> protocol error,
  472. // since there is data after the response line
  473. //
  474. //
  475. // if the previous character was an EOL, then the next one must
  476. // be the start of the next line. Reinitialize the response-
  477. // decoding state
  478. //
  479. if (fAtEOL) {
  480. fLastLineDigitsSeen = TRUE;
  481. nNumericReply = 0;
  482. idxPosInLine = 0;
  483. fAtEOL = FALSE;
  484. }
  485. //
  486. // if (so far) this line has been of the form we expect for a
  487. // response last-line, check the chars we just received to see
  488. // if it maintains that form
  489. //
  490. // since this form is completely determined by the first four
  491. // characters of the line, limit the checking to those chars
  492. //
  493. if (fLastLineDigitsSeen && (idxPosInLine < 4)) {
  494. if (idxPosInLine < 3) {
  495. //
  496. // if one of the first three chars is not a digit, mark
  497. // this line as a non-response
  498. //
  499. if (!isdigit(pchBuffer[bytesChecked])) {
  500. fLastLineDigitsSeen = FALSE;
  501. } else {
  502. //
  503. // store the numeric reply in an int
  504. //
  505. nNumericReply *= 10;
  506. nNumericReply += (int)(pchBuffer[bytesChecked] - '0');
  507. }
  508. } else if (idxPosInLine == 3) {
  509. //
  510. // the first three chars must be digits. If the fourth is a
  511. // space, then this line has the right form
  512. //
  513. if (pchBuffer[bytesChecked] != ' ') {
  514. fLastLineDigitsSeen = FALSE;
  515. }
  516. }
  517. }
  518. //
  519. // if at the end of a line, reset vars for the next line
  520. //
  521. if (pchBuffer[bytesChecked] == '\n') {
  522. DEBUG_PRINT(PROTOCOL,
  523. INFO,
  524. ("At EOL, and fDigits = %B\n",
  525. fLastLineDigitsSeen
  526. ));
  527. fAtEOL = TRUE;
  528. }
  529. }
  530. }
  531. //
  532. // if this data is expected to end in a proper response line, and that
  533. // line has been received, stop receiving more data
  534. //
  535. if (fEndOfLineCheck && fLastLineDigitsSeen && fAtEOL) {
  536. //
  537. // we found the end of the response, although we may not have
  538. // received an EOF(transmission) indication, because the server
  539. // didn't close the connection. Zero terminate the buffer
  540. //
  541. INET_ASSERT(bufferLeft > 0);
  542. pchBuffer[bytesReceived] = '\0';
  543. //
  544. // tear the numeric reply up by digit, placing it into our nicer
  545. // reply structure
  546. //
  547. INET_ASSERT(nNumericReply >= 0);
  548. INET_ASSERT(nNumericReply < 1000);
  549. prcResponse->Major = (nNumericReply / 100) % 10;
  550. prcResponse->Minor = (nNumericReply / 10) % 10;
  551. prcResponse->Detail = (nNumericReply) % 10;
  552. error = ERROR_SUCCESS;
  553. goto done;
  554. }
  555. //
  556. // if we were receiving data that was supposed to end in an EOL then this
  557. // is an error condition. Otherwise, it is the expected way to signal the
  558. // end of transmission
  559. //
  560. if (eofData) {
  561. if (fEndOfLineCheck) {
  562. error = ERROR_FTP_DROPPED;
  563. goto quit;
  564. } else {
  565. //
  566. // caller just receiving data. We're done
  567. //
  568. error = ERROR_SUCCESS;
  569. goto done;
  570. }
  571. }
  572. }
  573. INET_ASSERT(FALSE);
  574. quit:
  575. INET_ASSERT(error != ERROR_SUCCESS);
  576. if (pchBuffer != NULL) {
  577. pchBuffer = (LPSTR)FREE_MEMORY((HLOCAL)pchBuffer);
  578. INET_ASSERT(pchBuffer == NULL);
  579. }
  580. done:
  581. *lpBuffer = pchBuffer;
  582. *lpdwBufferLength = bytesReceived;
  583. INET_ASSERT((pchBuffer != NULL) ? (*(LPDWORD)pchBuffer != 0xc5c5c5c5) : TRUE);
  584. DEBUG_LEAVE(error);
  585. return error;
  586. }
  587. DWORD
  588. AbortTransfer(
  589. IN LPFTP_SESSION_INFO lpSessionInfo
  590. )
  591. /*++
  592. Routine Description:
  593. Aborts an ongoing transfer. Typically used to terminate a read file
  594. operation early. We don't expect a response from the server in this case
  595. Arguments:
  596. lpSessionInfo - pointer to FTP_SESSION_INFO containing context for sesion
  597. to abort
  598. Return Value:
  599. DWORD
  600. Success - ERROR_SUCCESS
  601. Failure - ERROR_INTERNET_EXTENDED_ERROR
  602. --*/
  603. {
  604. DEBUG_ENTER((DBG_FTP,
  605. Dword,
  606. "AbortTransfer",
  607. "%#x",
  608. lpSessionInfo
  609. ));
  610. //
  611. // get per-thread info block to determine blocking/non-blocking socket calls
  612. //
  613. LPINTERNET_THREAD_INFO lpThreadInfo = InternetGetThreadInfo();
  614. DWORD error;
  615. if (lpThreadInfo == NULL) {
  616. INET_ASSERT(FALSE);
  617. error = ERROR_INTERNET_INTERNAL_ERROR;
  618. goto quit;
  619. }
  620. //DWORD asyncFlags;
  621. //
  622. //asyncFlags = 0;
  623. //asyncFlags = lpThreadInfo->IsAsyncWorkerThread ? SF_NON_BLOCKING : 0;
  624. //
  625. // send an ABORT preceded by the following NVT/Telnet sequences:
  626. //
  627. // FF F4 = Interrupt Process (IP)
  628. // FF F2 = Data Mark (DM)
  629. //
  630. #define ABORT_COMMAND NVT_INTERRUPT_PROCESS_STRING \
  631. NVT_DATA_MARK_STRING \
  632. "ABOR" \
  633. "\r\n"
  634. //
  635. // BUGBUG - the IP/DM sequence should be sent as URGENT data
  636. //
  637. error = lpSessionInfo->socketControl->Send((LPBYTE)ABORT_COMMAND,
  638. sizeof(ABORT_COMMAND) - 1,
  639. SF_INDICATE
  640. );
  641. quit:
  642. DEBUG_LEAVE(error);
  643. return error;
  644. }
  645. //
  646. // private functions
  647. //
  648. PRIVATE
  649. DWORD
  650. __cdecl
  651. SendCommand(
  652. IN ICSocket * Socket,
  653. IN LPCSTR lpszFormat,
  654. IN ...
  655. )
  656. /*++
  657. Routine Description:
  658. Sends a command string to the FTP server. Wrapper for I_SendCommand()
  659. Arguments:
  660. Socket - socket to send data on
  661. lpszFormat - printf-style format string
  662. ... - arguments for lpszFormat
  663. Return Value:
  664. DWORD
  665. Success - ERROR_SUCCESS
  666. Failure - WSA error
  667. --*/
  668. {
  669. DEBUG_ENTER((DBG_FTP,
  670. Dword,
  671. "SendCommand",
  672. "%#x, %q",
  673. Socket,
  674. lpszFormat
  675. ));
  676. va_list arglist;
  677. DWORD error;
  678. va_start(arglist, lpszFormat);
  679. error = I_SendCommand(Socket,
  680. lpszFormat,
  681. arglist
  682. );
  683. va_end(arglist);
  684. DEBUG_LEAVE(error);
  685. return error;
  686. }
  687. PRIVATE
  688. DWORD
  689. I_SendCommand(
  690. IN ICSocket * Socket,
  691. IN LPCSTR lpszFormat,
  692. va_list arglist
  693. )
  694. /*++
  695. Routine Description:
  696. Sends a command string to a server
  697. Arguments:
  698. Socket - socket to send command on
  699. lpszFormat - printf-style format string for command
  700. arglist - variable list of arguments for format string
  701. Return Value:
  702. DWORD
  703. Success - ERROR_SUCCESS
  704. Failure - WSA error
  705. error returned from Windows Sockets
  706. ERROR_INTERNET_INTERNAL_ERROR
  707. The string was too large to fit into our stack buffer
  708. --*/
  709. {
  710. DEBUG_ENTER((DBG_FTP,
  711. Dword,
  712. "I_SendCommand",
  713. "%#x, %q, %#x",
  714. Socket,
  715. lpszFormat,
  716. arglist
  717. ));
  718. #define I_SEND_COMMAND_BUFFER_LENGTH 2048 // Arbitrary (but large)
  719. //
  720. // get per-thread info block to determine blocking/non-blocking socket calls
  721. //
  722. LPINTERNET_THREAD_INFO lpThreadInfo = InternetGetThreadInfo();
  723. DWORD error;
  724. if (lpThreadInfo == NULL) {
  725. INET_ASSERT(FALSE);
  726. error = ERROR_INTERNET_INTERNAL_ERROR;
  727. goto quit;
  728. }
  729. //
  730. // we need a buffer large enough to be able to handle the longest path plus
  731. // the command overhead. Typically, we will only be getting short strings
  732. // (USER, PORT, etc.)
  733. //
  734. //
  735. // BUGBUG
  736. // use _vsnprintf() to create the buffer - this allows us to specify the
  737. // maximum offset in the buffer, so avoiding a stack crash but this
  738. // brings in the cruntimes which we don't want to do.
  739. //
  740. CHAR buf[I_SEND_COMMAND_BUFFER_LENGTH];
  741. {
  742. int numChars = wvnsprintf(buf, I_SEND_COMMAND_BUFFER_LENGTH, lpszFormat, arglist);
  743. INET_ASSERT(numChars <= I_SEND_COMMAND_BUFFER_LENGTH - 2);
  744. INET_ASSERT(numChars > 0);
  745. if (numChars <= I_SEND_COMMAND_BUFFER_LENGTH - 2) {
  746. //
  747. // append trailing "\r\n"
  748. //
  749. buf[numChars++] = '\r';
  750. buf[numChars++] = '\n';
  751. error = Socket->Send((LPVOID)buf, numChars, SF_INDICATE);
  752. } else {
  753. DEBUG_PRINT(PROTOCOL,
  754. ERROR,
  755. ("%d chars blows internal buffer limit (%d chars)\n",
  756. numChars,
  757. I_SEND_COMMAND_BUFFER_LENGTH
  758. ));
  759. error = ERROR_INVALID_PARAMETER;
  760. }
  761. }
  762. quit:
  763. DEBUG_LEAVE(error);
  764. return error;
  765. }
  766. PRIVATE
  767. DWORD
  768. I_AttemptDataNegotiation(
  769. IN LPFTP_SESSION_INFO lpSessionInfo,
  770. IN DWORD dwFlags,
  771. IN OUT FTP_RESPONSE_CODE *prcResponse,
  772. IN LPCSTR lpszCommandFormat,
  773. IN va_list arglist
  774. )
  775. /*++
  776. Routine Description:
  777. Attempts to generate a data connection with server. If passive mode is
  778. selected & passive mode fails, then we back-down to non-passive
  779. Arguments:
  780. lpSessionInfo - pointer to FTP_SESSION_INFO describing server to
  781. connect to
  782. dwFlags - controlling how to transfer data between the client
  783. and server data connection, and how to set up data
  784. connection
  785. prcResponse - pointer to response code variable
  786. lpszCommandFormat - command to send
  787. arglist - any arguments for command
  788. Return Value:
  789. DWORD
  790. Success - ERROR_SUCCESS
  791. Failure - same as I_NegotiateDataConnection
  792. --*/
  793. {
  794. DEBUG_ENTER((DBG_FTP,
  795. Dword,
  796. "I_AttemptDataNegotiation",
  797. "%#x, %#x, %#x, %q, %#x",
  798. lpSessionInfo,
  799. dwFlags,
  800. prcResponse,
  801. lpszCommandFormat,
  802. arglist
  803. ));
  804. //
  805. // first try it without overriding passive mode
  806. //
  807. DWORD error = I_NegotiateDataConnection(lpSessionInfo,
  808. dwFlags,
  809. prcResponse,
  810. lpszCommandFormat,
  811. arglist,
  812. FALSE
  813. );
  814. //
  815. // if the negotiation failed because passive mode wasn't supported or not
  816. // allowed then try non-passive (if ok to do so)
  817. //
  818. if (error == ERROR_FTP_NO_PASSIVE_MODE) {
  819. INET_ASSERT(IsPassiveModeSession(lpSessionInfo));
  820. error = I_NegotiateDataConnection(lpSessionInfo,
  821. dwFlags,
  822. prcResponse,
  823. lpszCommandFormat,
  824. arglist,
  825. TRUE // no passive mode this time
  826. );
  827. }
  828. DEBUG_LEAVE(error);
  829. return error;
  830. }
  831. PRIVATE
  832. DWORD
  833. I_NegotiateDataConnection(
  834. IN LPFTP_SESSION_INFO lpSessionInfo,
  835. IN DWORD dwFlags,
  836. IN OUT FTP_RESPONSE_CODE *prcResponse,
  837. IN LPCSTR lpszCommandFormat,
  838. IN va_list arglist,
  839. IN BOOL bOverridePassive
  840. )
  841. /*++
  842. Routine Description:
  843. Sends a command to the FTP server and opens a data channel
  844. Arguments:
  845. lpSessionInfo - pointer to FTP_SESSION_INFO describing server to
  846. connect to
  847. dwFlags - controlling how to transfer data between the client
  848. and server data connection, and how to set up data
  849. connection
  850. prcResponse - pointer to response code variable
  851. lpszCommandFormat - command to send
  852. arglist - any arguments for command
  853. bOverridePassive - TRUE if OK to override passive mode (set at connect
  854. level)
  855. Return Value:
  856. DWORD
  857. Success - ERROR_SUCCESS
  858. Failure - WSA error
  859. --*/
  860. {
  861. DEBUG_ENTER((DBG_FTP,
  862. Dword,
  863. "I_NegotiateDataConnection",
  864. "%#x, %#x, %#x, %q, %#x, %B",
  865. lpSessionInfo,
  866. dwFlags,
  867. prcResponse,
  868. lpszCommandFormat,
  869. arglist,
  870. bOverridePassive
  871. ));
  872. PUCHAR puchAddr;
  873. PCHAR pch;
  874. ICSocket * socketControl;
  875. ICSocket * socketData ;
  876. ICSocket * socketListener ;
  877. DWORD error;
  878. int serr;
  879. LPINTERNET_THREAD_INFO lpThreadInfo = InternetGetThreadInfo();
  880. BOOL isAsync;
  881. BOOL bPassiveMode = FALSE;
  882. if (lpThreadInfo == NULL) {
  883. INET_ASSERT(FALSE);
  884. error = ERROR_INTERNET_INTERNAL_ERROR;
  885. goto quit;
  886. }
  887. isAsync = lpThreadInfo->IsAsyncWorkerThread;
  888. //Command(lpSessionInfo,
  889. // FALSE,
  890. // FTP_TRANSFER_TYPE_UNKNOWN,
  891. // prcResponse,
  892. // "MODE B"
  893. //// "MODE S"
  894. // );
  895. //
  896. // tell the server the type of transfer we want - ASCII ('A') or Binary ('I')
  897. //
  898. error = Command(lpSessionInfo,
  899. FALSE,
  900. FTP_TRANSFER_TYPE_UNKNOWN,
  901. prcResponse,
  902. "TYPE %s",
  903. ((dwFlags & FTP_TRANSFER_TYPE_MASK)
  904. == FTP_TRANSFER_TYPE_ASCII) ? "A" : "I"
  905. );
  906. if (error != ERROR_SUCCESS) {
  907. goto quit;
  908. }
  909. if (prcResponse->Major != FTP_RESPONSE_COMPLETE) {
  910. error = ERROR_INTERNET_EXTENDED_ERROR;
  911. goto quit;
  912. }
  913. socketControl = lpSessionInfo->socketControl;
  914. socketData = lpSessionInfo->socketData;
  915. socketListener = lpSessionInfo->socketListener;
  916. INET_ASSERT(!socketData->IsValid());
  917. INET_ASSERT(!socketListener->IsValid());
  918. //
  919. // Get the address of our control socket. We need to know what address
  920. // family (IPv4 or IPv6) to use for our data connection. And in the
  921. // active case, we need to provide this address to the server.
  922. //
  923. SOCKADDR_STORAGE ourCtrlAddr;
  924. error = socketControl->GetSockName((LPSOCKADDR)&ourCtrlAddr,
  925. sizeof(ourCtrlAddr));
  926. if (error != ERROR_SUCCESS) {
  927. goto error_exit;
  928. }
  929. //
  930. // we make passive connection if passive mode was specified in
  931. // InternetConnect() AND we are not overriding it
  932. //
  933. bPassiveMode = IsPassiveModeSession(lpSessionInfo) && !bOverridePassive;
  934. //
  935. // make the required type of data connection
  936. //
  937. if (!bPassiveMode) {
  938. SOCKADDR_STORAGE ourDataAddr;
  939. int cbAddrLen;
  940. //
  941. // standard (non-passive (ACTIVE?)) transfers. We create a socket and
  942. // tell the server which socket to connect to. The server then initiates
  943. // the connection with this client
  944. //
  945. error = socketListener->CreateSocket(0, ourCtrlAddr.ss_family,
  946. SOCK_STREAM, IPPROTO_TCP);
  947. if (error != ERROR_SUCCESS) {
  948. goto error_exit;
  949. }
  950. #if INET_DEBUG
  951. socketListener->SetSourcePort();
  952. #endif
  953. error = socketListener->Listen();
  954. if (error != ERROR_SUCCESS) {
  955. goto error_exit;
  956. }
  957. //
  958. // Now tell the server what address and port number it should use to
  959. // connect to us. The address is the local side of our control
  960. // connection, and the port number is that of the listening socket.
  961. //
  962. error = socketListener->GetSockName((LPSOCKADDR)&ourDataAddr,
  963. sizeof(ourDataAddr));
  964. if (error != ERROR_SUCCESS) {
  965. goto error_exit;
  966. }
  967. //
  968. // BUGBUG - using (global) winsock ntohs() (i.e. not SOCKS)
  969. //
  970. u_short port;
  971. if (ourCtrlAddr.ss_family == AF_INET) {
  972. //
  973. // Legacy IPv4. The PORT command consists of our IP address
  974. // formatted as 4 decimal numbers (one per byte) followed by
  975. // the port number as 2 decimals numbers (one per byte).
  976. // Byte order is big-endian.
  977. //
  978. // REVIEW: RFC 2428 describes the EPRT command, which is
  979. // intended to replace the PORT command. Should we try it
  980. // first? At the moment, we only attempt EPRT over IPv6.
  981. //
  982. port = _I_ntohs(((LPSOCKADDR_IN)&ourDataAddr)->sin_port);
  983. puchAddr = (PUCHAR)&((LPSOCKADDR_IN)&ourCtrlAddr)->sin_addr;
  984. error = SendCommand(socketControl,
  985. "PORT %d,%d,%d,%d,%d,%d",
  986. puchAddr[0],
  987. puchAddr[1],
  988. puchAddr[2],
  989. puchAddr[3],
  990. HIBYTE(port),
  991. LOBYTE(port)
  992. );
  993. } else {
  994. //
  995. // IPv6. The EPRT command is defined in RFC 2428, and has the
  996. // following format:
  997. // EPRT<space><d><address-family><d><address><d><port><d>
  998. // where <d> is a delimiter character ('|' recommended),
  999. // <address-family> is an IANA defined number (1=IPv4, 2=IPv6),
  1000. // <address> is the IP address in common string notation,
  1001. // <port> is the port number in common string notation.
  1002. //
  1003. #define MAX_IPV6_ADDR_LIT_LEN (sizeof("1111:2222:3333:4444:5555:6666:255.255.255.255%4294967295"))
  1004. char Address[MAX_IPV6_ADDR_LIT_LEN];
  1005. error = _I_getnameinfo((LPSOCKADDR)&ourCtrlAddr,
  1006. sizeof(SOCKADDR_IN6), Address,
  1007. sizeof(Address), NULL, 0, NI_NUMERICHOST);
  1008. if (error) {
  1009. goto error_exit;
  1010. }
  1011. port = _I_ntohs(((LPSOCKADDR_IN6)&ourDataAddr)->sin6_port);
  1012. error = SendCommand(socketControl, "EPRT |2|%s|%u", Address, port);
  1013. }
  1014. if (error != ERROR_SUCCESS) {
  1015. goto error_exit;
  1016. }
  1017. //
  1018. // read the response from the server. We are expecting something along
  1019. // the lines: "200 PORT Command Successful"
  1020. //
  1021. error = GetReply(lpSessionInfo, prcResponse);
  1022. if (error != ERROR_SUCCESS) {
  1023. goto error_exit;
  1024. }
  1025. if (prcResponse->Major != FTP_RESPONSE_COMPLETE) {
  1026. //
  1027. // Some IPv6 servers use LPRT instead of EPRT. So we try that.
  1028. // REVIEW: If we change the IPv4 case above to try EPRT first
  1029. // (see REVIEW comment above) then we'd try PORT here for IPv4.
  1030. //
  1031. if ((ourCtrlAddr.ss_family == AF_INET6) &&
  1032. (prcResponse->Status == FTP_RESPONSE_CMD_NOT_IMPL) ||
  1033. (prcResponse->Status == FTP_RESPONSE_CMD_SYNTAX_ERROR)) {
  1034. error = SendCommand(socketControl,
  1035. "LPRT 6,16,%u,%u,%u,%u,"
  1036. "%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,"
  1037. "2,%u,%u",
  1038. ((LPSOCKADDR_IN6)(&ourCtrlAddr))->sin6_addr.s6_addr[0],
  1039. ((LPSOCKADDR_IN6)(&ourCtrlAddr))->sin6_addr.s6_addr[1],
  1040. ((LPSOCKADDR_IN6)(&ourCtrlAddr))->sin6_addr.s6_addr[2],
  1041. ((LPSOCKADDR_IN6)(&ourCtrlAddr))->sin6_addr.s6_addr[3],
  1042. ((LPSOCKADDR_IN6)(&ourCtrlAddr))->sin6_addr.s6_addr[4],
  1043. ((LPSOCKADDR_IN6)(&ourCtrlAddr))->sin6_addr.s6_addr[5],
  1044. ((LPSOCKADDR_IN6)(&ourCtrlAddr))->sin6_addr.s6_addr[6],
  1045. ((LPSOCKADDR_IN6)(&ourCtrlAddr))->sin6_addr.s6_addr[7],
  1046. ((LPSOCKADDR_IN6)(&ourCtrlAddr))->sin6_addr.s6_addr[8],
  1047. ((LPSOCKADDR_IN6)(&ourCtrlAddr))->sin6_addr.s6_addr[9],
  1048. ((LPSOCKADDR_IN6)(&ourCtrlAddr))->sin6_addr.s6_addr[10],
  1049. ((LPSOCKADDR_IN6)(&ourCtrlAddr))->sin6_addr.s6_addr[11],
  1050. ((LPSOCKADDR_IN6)(&ourCtrlAddr))->sin6_addr.s6_addr[12],
  1051. ((LPSOCKADDR_IN6)(&ourCtrlAddr))->sin6_addr.s6_addr[13],
  1052. ((LPSOCKADDR_IN6)(&ourCtrlAddr))->sin6_addr.s6_addr[14],
  1053. ((LPSOCKADDR_IN6)(&ourCtrlAddr))->sin6_addr.s6_addr[15],
  1054. HIBYTE(port), LOBYTE(port)
  1055. );
  1056. if (error != ERROR_SUCCESS) {
  1057. goto error_exit;
  1058. }
  1059. error = GetReply(lpSessionInfo, prcResponse);
  1060. if (error != ERROR_SUCCESS) {
  1061. goto error_exit;
  1062. }
  1063. if (prcResponse->Major == FTP_RESPONSE_COMPLETE)
  1064. goto GotActiveReply;
  1065. }
  1066. error = ERROR_INTERNET_EXTENDED_ERROR;
  1067. goto error_exit;
  1068. }
  1069. } else {
  1070. PCHAR pchTemp;
  1071. //
  1072. // PASSIVE mode. Due to problems with firewalls not allowing incoming
  1073. // connection requests, we have to ask the server to create a new socket
  1074. // which we then connect to. This is the inverse of the non-PASV connect
  1075. // case above
  1076. //
  1077. error = socketData->CreateSocket(0, ourCtrlAddr.ss_family,
  1078. SOCK_STREAM, IPPROTO_TCP);
  1079. if (error != ERROR_SUCCESS) {
  1080. goto error_exit;
  1081. }
  1082. //
  1083. // send the PASV request to the server
  1084. //
  1085. if (ourCtrlAddr.ss_family == AF_INET) {
  1086. // REVIEW: Attempt EPSV first for IPv4 as well?
  1087. error = SendCommand(socketControl, "PASV");
  1088. } else {
  1089. error = SendCommand(socketControl, "EPSV");
  1090. }
  1091. if (error != ERROR_SUCCESS) {
  1092. goto error_exit;
  1093. }
  1094. //
  1095. // Read the response. For the "PASV" command, we are expecting it
  1096. // to return the full address information for the port in the form
  1097. // "227 (h1,h2,h3,h4,p1,p2)"
  1098. // meaning that the server is entering PASSIVE mode (227), and the
  1099. // IP address and socket to connect to being h1.h2.h3.h4, p1p2.
  1100. //
  1101. // For the "EPSV" command, we are expecting a response of the form
  1102. // "229 (<d><d><d><port><d>)"
  1103. // meaning that the server is entering PASSIVE mode (229), the
  1104. // IP address to connect to is that of the control connection, and
  1105. // the port number to connect to is given by <port>.
  1106. //
  1107. error = GetReply(lpSessionInfo, prcResponse);
  1108. if (error != ERROR_SUCCESS) {
  1109. goto error_exit;
  1110. }
  1111. if (prcResponse->Major != FTP_RESPONSE_COMPLETE) {
  1112. //
  1113. // Some IPv6 servers use LPSV instead of EPSV.
  1114. // The wonderful thing about standards is that
  1115. // there are so many to choose from.
  1116. //
  1117. if ((ourCtrlAddr.ss_family == AF_INET6) &&
  1118. ((prcResponse->Status == FTP_RESPONSE_CMD_NOT_IMPL) ||
  1119. (prcResponse->Status == FTP_RESPONSE_CMD_SYNTAX_ERROR))) {
  1120. error = SendCommand(socketControl, "LPSV");
  1121. if (error != ERROR_SUCCESS) {
  1122. goto error_exit;
  1123. }
  1124. error = GetReply(lpSessionInfo, prcResponse);
  1125. if (error != ERROR_SUCCESS) {
  1126. goto error_exit;
  1127. }
  1128. if (prcResponse->Major == FTP_RESPONSE_COMPLETE)
  1129. goto GotPassiveReply;
  1130. }
  1131. //
  1132. // If we get 500 or 502 (command not implemented), 425
  1133. // (can't open data connection), or 530 (not logged in)
  1134. // then return ERROR_FTP_NO_PASSIVE_MODE, else
  1135. // ERROR_INTERNET_EXTENDED_ERROR.
  1136. //
  1137. if ((prcResponse->Status == FTP_RESPONSE_CANT_OPEN_DATA)
  1138. || (prcResponse->Status == FTP_RESPONSE_CMD_NOT_IMPL)
  1139. || (prcResponse->Status == FTP_RESPONSE_CMD_SYNTAX_ERROR)
  1140. || (prcResponse->Status == FTP_RESPONSE_NOT_LOGGED_IN)) {
  1141. error = ERROR_FTP_NO_PASSIVE_MODE;
  1142. } else {
  1143. error = ERROR_INTERNET_EXTENDED_ERROR;
  1144. }
  1145. goto error_exit;
  1146. }
  1147. GotPassiveReply:
  1148. //
  1149. // parse the endpoint out of the server response (stored in the per-
  1150. // thread last response info buffer).
  1151. //
  1152. // If we fail to lock the response text, then that's an internal error.
  1153. // If we fail to parse the required information out of the response text
  1154. // then we return an extended error indication, since presumably the
  1155. // server sent response text, but not what we were expecting
  1156. //
  1157. pch = (PCHAR)InternetLockErrorText();
  1158. if (pch != NULL) {
  1159. if (ourCtrlAddr.ss_family == AF_INET) {
  1160. //
  1161. // Parse respone to PASV command.
  1162. //
  1163. pch = strstr(pch, "227 ");
  1164. if (pch != NULL) {
  1165. pch += sizeof("227 ") - 1;
  1166. while (!isdigit(*pch) && *pch) {
  1167. ++pch;
  1168. }
  1169. if (isdigit(*pch)) {
  1170. int i;
  1171. DWORD octets[6];
  1172. //
  1173. // parse the individual address parts out of the response
  1174. //
  1175. for (i = 0; (i < ARRAY_ELEMENTS(octets)) && isdigit(*pch); ++i) {
  1176. if (!ExtractInt(&pch, 0, (LPINT)&octets[i])) {
  1177. break;
  1178. }
  1179. if (octets[i] > 255) {
  1180. break;
  1181. }
  1182. if (i < ARRAY_ELEMENTS(octets) - 1) {
  1183. while (!isdigit(*pch) && *pch) {
  1184. ++pch;
  1185. }
  1186. }
  1187. }
  1188. //
  1189. // if we successfully parsed the server's address info then
  1190. // try to connect with the address it sent us
  1191. //
  1192. if ((i == ARRAY_ELEMENTS(octets)) && (octets[i - 1] <= 255)) {
  1193. SOCKADDR_IN remoteSockaddr;
  1194. memset(&remoteSockaddr, 0, sizeof(remoteSockaddr));
  1195. remoteSockaddr.sin_family = AF_INET;
  1196. //
  1197. // N.B. Using (global) winsock ntohs() (i.e. not SOCKS)
  1198. //
  1199. remoteSockaddr.sin_port = _I_htons(
  1200. (USHORT)((octets[4] << 8)
  1201. | (USHORT)octets[5]));
  1202. puchAddr = (PUCHAR)&remoteSockaddr.sin_addr;
  1203. puchAddr[0] = (UCHAR)octets[0];
  1204. puchAddr[1] = (UCHAR)octets[1];
  1205. puchAddr[2] = (UCHAR)octets[2];
  1206. puchAddr[3] = (UCHAR)octets[3];
  1207. error = socketData->DirectConnect(
  1208. (LPSOCKADDR)&remoteSockaddr);
  1209. } else {
  1210. DEBUG_PRINT(PROTOCOL,
  1211. ERROR,
  1212. ("failed to parse 6 address elements\n"
  1213. ));
  1214. error = ERROR_INTERNET_EXTENDED_ERROR;
  1215. }
  1216. } else {
  1217. DEBUG_PRINT(PROTOCOL,
  1218. ERROR,
  1219. ("failed to locate start of address info in response text\n"
  1220. ));
  1221. error = ERROR_INTERNET_EXTENDED_ERROR;
  1222. }
  1223. } else {
  1224. DEBUG_PRINT(PROTOCOL,
  1225. ERROR,
  1226. ("failed to locate \"227\" in response text\n"
  1227. ));
  1228. error = ERROR_INTERNET_EXTENDED_ERROR;
  1229. }
  1230. } else {
  1231. CHAR delim;
  1232. //
  1233. // Parse respone to EPSV or LPSV command.
  1234. //
  1235. pchTemp = strstr(pch, "229 ");
  1236. if (pchTemp != NULL) {
  1237. pch = pchTemp + sizeof("229 ") - 1;
  1238. while ((*pch != '(') && (*pch != '\0')) {
  1239. ++pch;
  1240. }
  1241. if ((pch[0] == '(') &&
  1242. ((delim = pch[1]) != '\0') &&
  1243. (pch[2] == delim) &&
  1244. (pch[3] == delim)) {
  1245. DWORD port;
  1246. //
  1247. // parse the port out of the response
  1248. //
  1249. pch += 4;
  1250. if (ExtractInt(&pch, 0, (LPINT)&port)) {
  1251. //
  1252. // if we successfully parsed the port then
  1253. // try to connect with it
  1254. //
  1255. SOCKADDR_IN6 remoteSockaddr;
  1256. error = socketControl->GetPeerName(
  1257. (LPSOCKADDR)&remoteSockaddr,
  1258. sizeof(SOCKADDR_IN6));
  1259. if (error == ERROR_SUCCESS) {
  1260. //
  1261. // N.B. Using (global) winsock ntohs()
  1262. // (i.e. not SOCKS)
  1263. //
  1264. remoteSockaddr.sin6_port =
  1265. _I_htons((USHORT)port);
  1266. error = socketData->DirectConnect(
  1267. (LPSOCKADDR)&remoteSockaddr);
  1268. }
  1269. } else {
  1270. DEBUG_PRINT(PROTOCOL,
  1271. ERROR,
  1272. ("failed to parse port\n"
  1273. ));
  1274. error = ERROR_INTERNET_EXTENDED_ERROR;
  1275. }
  1276. } else {
  1277. DEBUG_PRINT(PROTOCOL,
  1278. ERROR,
  1279. ("failed to locate start of address info in response text\n"
  1280. ));
  1281. error = ERROR_INTERNET_EXTENDED_ERROR;
  1282. }
  1283. } else if ((pchTemp = strstr(pch, "228 ")) != NULL) {
  1284. pch = pchTemp + sizeof("228 ") - 1;
  1285. while (!isdigit(*pch) && *pch) {
  1286. ++pch;
  1287. }
  1288. if (isdigit(*pch)) {
  1289. int i;
  1290. DWORD octets[21];
  1291. //
  1292. // Parse the individual address parts out of
  1293. // the response.
  1294. //
  1295. for (i = 0; (i < ARRAY_ELEMENTS(octets)) && isdigit(*pch); ++i) {
  1296. if (!ExtractInt(&pch, 0, (LPINT)&octets[i])) {
  1297. break;
  1298. }
  1299. if (octets[i] > 255) {
  1300. break;
  1301. }
  1302. if (i < ARRAY_ELEMENTS(octets) - 1) {
  1303. while (!isdigit(*pch) && *pch) {
  1304. ++pch;
  1305. }
  1306. }
  1307. }
  1308. //
  1309. // If we successfully parsed the server's address info
  1310. // then try to connect with the address it sent us.
  1311. //
  1312. if ((i == ARRAY_ELEMENTS(octets)) &&
  1313. (octets[i - 1] <= 255) &&
  1314. (octets[0] == 6) &&
  1315. (octets[1] == 16) &&
  1316. (octets[18] == 2)) {
  1317. SOCKADDR_IN6 remoteSockaddr;
  1318. memset(&remoteSockaddr, 0, sizeof remoteSockaddr);
  1319. remoteSockaddr.sin6_family = AF_INET6;
  1320. //
  1321. // N.B. Using (global) winsock ntohs()
  1322. // (i.e. not SOCKS)
  1323. //
  1324. remoteSockaddr.sin6_port = _I_htons(
  1325. (USHORT)((octets[19] << 8)
  1326. | (USHORT)octets[20]));
  1327. for (i = 0; i < 16; i++)
  1328. remoteSockaddr.sin6_addr.s6_addr[i] = (UCHAR)octets[i + 2];
  1329. error = socketData->DirectConnect(
  1330. (LPSOCKADDR)&remoteSockaddr);
  1331. } else {
  1332. DEBUG_PRINT(PROTOCOL,
  1333. ERROR,
  1334. ("failed to parse 20 address elements\n"
  1335. ));
  1336. error = ERROR_INTERNET_EXTENDED_ERROR;
  1337. }
  1338. } else {
  1339. DEBUG_PRINT(PROTOCOL,
  1340. ERROR,
  1341. ("failed to locate start of address info in response text\n"
  1342. ));
  1343. error = ERROR_INTERNET_EXTENDED_ERROR;
  1344. }
  1345. } else {
  1346. DEBUG_PRINT(PROTOCOL,
  1347. ERROR,
  1348. ("failed to locate \"229\" or \"228\" in response text\n"
  1349. ));
  1350. error = ERROR_INTERNET_EXTENDED_ERROR;
  1351. }
  1352. }
  1353. //
  1354. // unlock the response text
  1355. //
  1356. //InternetUnlockErrorText();
  1357. } else {
  1358. DEBUG_PRINT(PROTOCOL,
  1359. ERROR,
  1360. ("failed to lock last response text\n"
  1361. ));
  1362. error = ERROR_INTERNET_INTERNAL_ERROR;
  1363. }
  1364. //
  1365. // bail out if we met with any errors
  1366. //
  1367. if (error != ERROR_SUCCESS) {
  1368. goto error_exit;
  1369. }
  1370. }
  1371. GotActiveReply:
  1372. //
  1373. // issue SIZE command to get file size
  1374. //
  1375. if(StrCmpNI(lpszCommandFormat, "RETR", 4) == 0) { // If we're downloading a file
  1376. error = I_SendCommand(socketControl,
  1377. "SIZE %s",
  1378. arglist
  1379. );
  1380. if (error != ERROR_SUCCESS) {
  1381. goto error_exit;
  1382. }
  1383. error = GetReply(lpSessionInfo, prcResponse);
  1384. if (error != ERROR_SUCCESS) {
  1385. if (socketData->IsValid()) {
  1386. ResetSocket(socketData);
  1387. }
  1388. goto error_exit;
  1389. }
  1390. if (prcResponse->Major == FTP_RESPONSE_COMPLETE) {
  1391. pch = (PCHAR)InternetLockErrorText();
  1392. if (pch != NULL) {
  1393. pch = strstr(pch, "213 ");
  1394. if (pch != NULL) {
  1395. pch += sizeof("213 ") - 1;
  1396. if ( pch )
  1397. {
  1398. if (*pch && isdigit(*pch)) {
  1399. if (ExtractInt(&pch, 0, (int *) &(lpSessionInfo->dwFileSizeLow))) {
  1400. lpSessionInfo->Flags |= FFTP_KNOWN_FILE_SIZE;
  1401. }
  1402. }
  1403. }
  1404. }
  1405. }
  1406. }
  1407. }
  1408. //
  1409. // we have the data connection, send the command
  1410. //
  1411. error = I_SendCommand(socketControl,
  1412. lpszCommandFormat,
  1413. arglist
  1414. );
  1415. if (error != ERROR_SUCCESS) {
  1416. goto error_exit;
  1417. }
  1418. //
  1419. // get the preliminary reply from the FTP server
  1420. //
  1421. error = GetReply(lpSessionInfo, prcResponse);
  1422. if (error != ERROR_SUCCESS) {
  1423. if (socketData->IsValid()) {
  1424. ResetSocket(socketData);
  1425. }
  1426. goto error_exit;
  1427. }
  1428. if ((prcResponse->Major != FTP_RESPONSE_PRELIMINARY)
  1429. && (prcResponse->Major != FTP_RESPONSE_COMPLETE)) {
  1430. if (socketData->IsValid()) {
  1431. ResetSocket(socketData);
  1432. }
  1433. error = ERROR_INTERNET_EXTENDED_ERROR;
  1434. goto error_exit;
  1435. }
  1436. //
  1437. // Parse out file size if its around
  1438. // Assumes the substring containing the file size looks like; "(99999 bytes)"
  1439. //
  1440. if(!(lpSessionInfo->Flags & FFTP_KNOWN_FILE_SIZE)) { // Don't already have the size
  1441. pch = (PCHAR)InternetLockErrorText();
  1442. if (pch != NULL) {
  1443. pch = strstr(pch, "150 ");
  1444. if (pch != NULL) {
  1445. pch += sizeof("150 ") - 1;
  1446. PCHAR pchEnd = strstr(pch, " bytes)");
  1447. if (pchEnd) {
  1448. DWORD dwFileSize = 0;
  1449. DWORD dwMul = 1;
  1450. PCHAR pchBeg = pch;
  1451. pch = pchEnd - 1;
  1452. while (pch && (pch > pchBeg) && isdigit(*pch)) {
  1453. dwFileSize += ((*pch - '0') * dwMul);
  1454. dwMul *= 10;
  1455. pch--;
  1456. }
  1457. if (pch && (*pch == '(') && (dwFileSize > 0)) {
  1458. lpSessionInfo->dwFileSizeLow = dwFileSize;
  1459. lpSessionInfo->Flags |= FFTP_KNOWN_FILE_SIZE;
  1460. }
  1461. }
  1462. }
  1463. }
  1464. }
  1465. //
  1466. // unlock the response text
  1467. //
  1468. //InternetUnlockErrorText();
  1469. //
  1470. // if we are expecting the server to create the connection (ACTIVE mode)
  1471. // then perform an accept() on our listening socket in order to establish
  1472. // the connection
  1473. //
  1474. if (!bPassiveMode) {
  1475. //
  1476. // if we just accept(), we may hang forever if the server doesn't call
  1477. // back (servers are all alike), so we use select() to wait for the
  1478. // socket to be acceptable before calling accept() proper
  1479. //
  1480. error = socketListener->SelectAccept(
  1481. *socketData,
  1482. //
  1483. // BUGBUG - call GetTimeoutValue()
  1484. //
  1485. GlobalFtpAcceptTimeout
  1486. );
  1487. if ( error != ERROR_SUCCESS ) {
  1488. goto error_exit;
  1489. }
  1490. //
  1491. // we no longer require the socket we created for listening for the
  1492. // incoming server connection request
  1493. //
  1494. INET_ASSERT(socketListener != socketControl);
  1495. INET_ASSERT(socketListener != socketData);
  1496. INET_ASSERT(socketListener->GetSocket() != socketControl->GetSocket());
  1497. INET_ASSERT(socketListener->GetSocket() != socketData->GetSocket());
  1498. #if INET_DEBUG
  1499. socketData->SetSourcePort();
  1500. #endif
  1501. socketListener->Close();
  1502. if (!socketData->IsValid()) {
  1503. goto error_exit;
  1504. }
  1505. }
  1506. //
  1507. // set send and receive timeouts for data socket. If we get an error, ignore
  1508. // it. Note this probably means that the socket is (somehow) invalid, and
  1509. // that we should really return an error. But for now (as with the other
  1510. // protocols), I will presume that the error is non-fatal (but note it)
  1511. //
  1512. socketData->SetTimeout(
  1513. SEND_TIMEOUT,
  1514. (int)GetTimeoutValue(INTERNET_OPTION_DATA_SEND_TIMEOUT)
  1515. );
  1516. socketData->SetTimeout(
  1517. RECEIVE_TIMEOUT,
  1518. (int)GetTimeoutValue(INTERNET_OPTION_DATA_RECEIVE_TIMEOUT)
  1519. );
  1520. INET_ASSERT(error == ERROR_SUCCESS);
  1521. INET_ASSERT(lpSessionInfo->socketData == socketData);
  1522. quit:
  1523. DEBUG_LEAVE(error);
  1524. return error;
  1525. error_exit:
  1526. if (socketData->IsValid()) {
  1527. socketData->Close();
  1528. }
  1529. if (socketListener->IsValid()) {
  1530. socketListener->Close();
  1531. }
  1532. goto quit;
  1533. }