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.

1327 lines
30 KiB

  1. /**********************************************************************/
  2. /** Microsoft Windows NT **/
  3. /** Copyright(c) Microsoft Corp., 1993 **/
  4. /**********************************************************************/
  5. /*
  6. sockutil.cxx
  7. This module contains utility routines for managing & manipulating
  8. sockets.
  9. Functions exported by this module:
  10. InitializeSockets
  11. TerminateSockets
  12. CreateDataSocket
  13. CreateFtpdSocket
  14. CloseSocket
  15. ResetSocket
  16. AcceptSocket
  17. SockSend
  18. SockRecv
  19. SockPrintf2
  20. ReplyToUser()
  21. SockReadLine
  22. SockMultilineMessage2
  23. FILE HISTORY:
  24. KeithMo 07-Mar-1993 Created.
  25. MuraliK April-1995 Misc modifications (removed usage of various
  26. socket functions/modified them)
  27. */
  28. #include "ftpdp.hxx"
  29. //
  30. // Private constants.
  31. //
  32. #define DEFAULT_BUFFER_SIZE 4096 // bytes
  33. //
  34. // Private globals.
  35. //
  36. //
  37. // Private prototypes.
  38. //
  39. SOCKERR
  40. vSockPrintf(
  41. LPUSER_DATA pUserData,
  42. SOCKET sock,
  43. LPCSTR pszFormat,
  44. va_list args
  45. );
  46. SOCKERR
  47. vSockReply(
  48. LPUSER_DATA pUserData,
  49. SOCKET sock,
  50. UINT ReplyCode,
  51. CHAR chSeparator,
  52. LPCSTR pszFormat,
  53. va_list args
  54. );
  55. BOOL
  56. vTelnetEscapeIAC(
  57. PCHAR pszBuffer,
  58. PINT pcchBufChars,
  59. INT ccbMaxLen
  60. );
  61. //
  62. // Public functions.
  63. //
  64. /*******************************************************************
  65. NAME: CreateDataSocket
  66. SYNOPSIS: Creates a data socket for the specified address & port.
  67. ENTRY: psock - Will receive the new socket ID if successful.
  68. addrLocal - The local Internet address for the socket
  69. in network byte order.
  70. portLocal - The local port for the socket in network
  71. byte order.
  72. addrRemote - The remote Internet address for the socket
  73. in network byte order.
  74. portRemote - The remote port for the socket in network
  75. byte order.
  76. RETURNS: SOCKERR - 0 if successful, !0 if not.
  77. HISTORY:
  78. KeithMo 10-Mar-1993 Created.
  79. KeithMo 07-Sep-1993 Enable SO_REUSEADDR.
  80. ********************************************************************/
  81. SOCKERR
  82. CreateDataSocket(
  83. SOCKET * psock,
  84. ULONG addrLocal,
  85. PORT portLocal,
  86. ULONG addrRemote,
  87. PORT portRemote
  88. )
  89. {
  90. SOCKET sNew = INVALID_SOCKET;
  91. SOCKERR serr = 0;
  92. SOCKADDR_IN sockAddr;
  93. //
  94. // Just to be paranoid...
  95. //
  96. DBG_ASSERT( psock != NULL );
  97. *psock = INVALID_SOCKET;
  98. //
  99. // Create the socket.
  100. //
  101. sNew = WSASocketW(AF_INET,
  102. SOCK_STREAM,
  103. IPPROTO_TCP,
  104. NULL,
  105. 0,
  106. WSA_FLAG_OVERLAPPED);
  107. serr = ( sNew == INVALID_SOCKET ) ? WSAGetLastError() : 0;
  108. if( serr == 0 )
  109. {
  110. BOOL fReuseAddr = TRUE;
  111. //
  112. // Since we always bind to the same local port,
  113. // allow the reuse of address/port pairs.
  114. //
  115. if( setsockopt( sNew,
  116. SOL_SOCKET,
  117. SO_REUSEADDR,
  118. (CHAR *)&fReuseAddr,
  119. sizeof(fReuseAddr) ) != 0 )
  120. {
  121. serr = WSAGetLastError();
  122. }
  123. }
  124. if( serr == 0 )
  125. {
  126. //
  127. // Bind the local internet address & port to the socket.
  128. //
  129. sockAddr.sin_family = AF_INET;
  130. sockAddr.sin_addr.s_addr = addrLocal;
  131. sockAddr.sin_port = portLocal;
  132. if( bind( sNew, (SOCKADDR *)&sockAddr, sizeof(sockAddr) ) != 0 )
  133. {
  134. serr = WSAGetLastError();
  135. }
  136. }
  137. if( serr == 0 )
  138. {
  139. //
  140. // Connect to the remote internet address & port.
  141. //
  142. sockAddr.sin_family = AF_INET;
  143. sockAddr.sin_addr.s_addr = addrRemote;
  144. sockAddr.sin_port = portRemote;
  145. if( connect( sNew, (SOCKADDR *)&sockAddr, sizeof(sockAddr) ) != 0 )
  146. {
  147. serr = WSAGetLastError();
  148. }
  149. }
  150. if( serr == 0 )
  151. {
  152. //
  153. // Success! Return the socket to the caller.
  154. //
  155. DBG_ASSERT( sNew != INVALID_SOCKET );
  156. *psock = sNew;
  157. IF_DEBUG( SOCKETS )
  158. {
  159. DBGPRINTF(( DBG_CONTEXT,
  160. "data socket %d connected from (%08lX,%04X) to (%08lX,%04X)\n",
  161. sNew,
  162. ntohl( addrLocal ),
  163. ntohs( portLocal ),
  164. ntohl( addrRemote ),
  165. ntohs( portRemote ) ));
  166. }
  167. }
  168. else
  169. {
  170. //
  171. // Something fatal happened. Close the socket if
  172. // managed to actually open it.
  173. //
  174. IF_DEBUG( SOCKETS )
  175. {
  176. DBGPRINTF(( DBG_CONTEXT,
  177. "no data socket from (%08lX,%04X) to (%08lX, %04X), error %d\n",
  178. ntohl( addrLocal ),
  179. ntohs( portLocal ),
  180. ntohl( addrRemote ),
  181. ntohs( portRemote ),
  182. serr ));
  183. }
  184. if( sNew != INVALID_SOCKET )
  185. {
  186. ResetSocket( sNew );
  187. }
  188. }
  189. return serr;
  190. } // CreateDataSocket
  191. /*******************************************************************
  192. NAME: CreateFtpdSocket
  193. SYNOPSIS: Creates a new socket at the FTPD port.
  194. This will be used by the passive data transfer.
  195. ENTRY: psock - Will receive the new socket ID if successful.
  196. addrLocal - The lcoal Internet address for the socket
  197. in network byte order.
  198. portLocal - The local port for the socket in network
  199. byte order.
  200. RETURNS: SOCKERR - 0 if successful, !0 if not.
  201. HISTORY:
  202. KeithMo 08-Mar-1993 Created.
  203. ********************************************************************/
  204. SOCKERR
  205. CreateFtpdSocket(
  206. SOCKET * psock,
  207. ULONG addrLocal,
  208. PORT portLocal,
  209. FTP_SERVER_INSTANCE *pInstance
  210. )
  211. {
  212. SOCKET sNew = INVALID_SOCKET;
  213. SOCKERR serr = 0;
  214. //
  215. // Just to be paranoid...
  216. //
  217. DBG_ASSERT( psock != NULL );
  218. *psock = INVALID_SOCKET;
  219. //
  220. // Create the connection socket.
  221. //
  222. sNew = WSASocketW(AF_INET,
  223. SOCK_STREAM,
  224. IPPROTO_TCP,
  225. NULL,
  226. 0,
  227. WSA_FLAG_OVERLAPPED);
  228. serr = ( sNew == INVALID_SOCKET ) ? WSAGetLastError() : 0;
  229. if( serr == 0 )
  230. {
  231. BOOL fReuseAddr = FALSE;
  232. //
  233. // Muck around with the socket options a bit.
  234. // Berkeley FTPD does this.
  235. //
  236. if( setsockopt( sNew,
  237. SOL_SOCKET,
  238. SO_REUSEADDR,
  239. (CHAR *)&fReuseAddr,
  240. sizeof(fReuseAddr) ) != 0 )
  241. {
  242. serr = WSAGetLastError();
  243. }
  244. }
  245. if( serr == 0 )
  246. {
  247. SOCKADDR_IN sockAddr;
  248. //
  249. // Bind an address to the socket.
  250. //
  251. sockAddr.sin_family = AF_INET;
  252. sockAddr.sin_addr.s_addr = addrLocal;
  253. sockAddr.sin_port = portLocal;
  254. if( bind( sNew, (SOCKADDR *)&sockAddr, sizeof(sockAddr) ) != 0 )
  255. {
  256. serr = WSAGetLastError();
  257. }
  258. }
  259. if( serr == 0 )
  260. {
  261. //
  262. // Put the socket into listen mode.
  263. //
  264. if( listen( sNew, (INT)pInstance->NumListenBacklog()) != 0 )
  265. {
  266. serr = WSAGetLastError();
  267. }
  268. }
  269. if( serr == 0 )
  270. {
  271. //
  272. // Success! Return the socket to the caller.
  273. //
  274. DBG_ASSERT( sNew != INVALID_SOCKET );
  275. *psock = sNew;
  276. IF_DEBUG( SOCKETS )
  277. {
  278. DBGPRINTF(( DBG_CONTEXT,
  279. "connection socket %d created at (%08lX,%04X)\n",
  280. sNew,
  281. ntohl( addrLocal ),
  282. ntohs( portLocal ) ));
  283. }
  284. }
  285. else
  286. {
  287. //
  288. // Something fatal happened. Close the socket if
  289. // managed to actually open it.
  290. //
  291. IF_DEBUG( SOCKETS )
  292. {
  293. DBGPRINTF(( DBG_CONTEXT,
  294. "no connection socket at (%08lX, %04X), error %d\n",
  295. ntohl( addrLocal ),
  296. ntohs( portLocal ),
  297. serr ));
  298. }
  299. if( sNew != INVALID_SOCKET )
  300. {
  301. ResetSocket( sNew );
  302. }
  303. }
  304. return serr;
  305. } // CreateFtpdSocket
  306. /*******************************************************************
  307. NAME: CloseSocket
  308. SYNOPSIS: Closes the specified socket. This is just a thin
  309. wrapper around the "real" closesocket() API.
  310. ENTRY: sock - The socket to close.
  311. RETURNS: SOCKERR - 0 if successful, !0 if not.
  312. HISTORY:
  313. KeithMo 26-Apr-1993 Created.
  314. ********************************************************************/
  315. SOCKERR
  316. CloseSocket(
  317. SOCKET sock
  318. )
  319. {
  320. SOCKERR serr = 0;
  321. //
  322. // Close the socket.
  323. //
  324. if( closesocket( sock ) != 0 )
  325. {
  326. serr = WSAGetLastError();
  327. }
  328. IF_DEBUG( SOCKETS )
  329. {
  330. if( serr == 0 )
  331. {
  332. DBGPRINTF(( DBG_CONTEXT,
  333. "closed socket %d\n",
  334. sock ));
  335. }
  336. else
  337. {
  338. DBGPRINTF(( DBG_CONTEXT,
  339. "cannot close socket %d, error %d\n",
  340. sock,
  341. serr ));
  342. }
  343. }
  344. return serr;
  345. } // CloseSocket
  346. /*******************************************************************
  347. NAME: ResetSocket
  348. SYNOPSIS: Performs a "hard" close on the given socket.
  349. ENTRY: sock - The socket to close.
  350. RETURNS: SOCKERR - 0 if successful, !0 if not.
  351. HISTORY:
  352. KeithMo 08-Mar-1993 Created.
  353. ********************************************************************/
  354. SOCKERR
  355. ResetSocket(
  356. SOCKET sock
  357. )
  358. {
  359. SOCKERR serr = 0;
  360. LINGER linger;
  361. //
  362. // Enable linger with a timeout of zero. This will
  363. // force the hard close when we call closesocket().
  364. //
  365. // We ignore the error return from setsockopt. If it
  366. // fails, we'll just try to close the socket anyway.
  367. //
  368. linger.l_onoff = TRUE;
  369. linger.l_linger = 0;
  370. setsockopt( sock,
  371. SOL_SOCKET,
  372. SO_LINGER,
  373. (CHAR *)&linger,
  374. sizeof(linger) );
  375. //
  376. // Close the socket.
  377. //
  378. if( closesocket( sock ) != 0 )
  379. {
  380. serr = WSAGetLastError();
  381. }
  382. IF_DEBUG( SOCKETS )
  383. {
  384. if( serr == 0 )
  385. {
  386. DBGPRINTF(( DBG_CONTEXT,
  387. "reset socket %d\n",
  388. sock ));
  389. }
  390. else
  391. {
  392. DBGPRINTF(( DBG_CONTEXT,
  393. "cannot reset socket %d, error %d\n",
  394. sock,
  395. serr ));
  396. }
  397. }
  398. return serr;
  399. } // ResetSocket
  400. /*******************************************************************
  401. NAME: AcceptSocket
  402. SYNOPSIS: Waits for a connection to the specified socket.
  403. The socket is assumed to be "listening".
  404. ENTRY: sockListen - The socket to accept on.
  405. psockNew - Will receive the newly "accepted" socket
  406. if successful.
  407. paddr - Will receive the client's network address.
  408. fEnforceTimeout - If TRUE, this routine will enforce
  409. the idle-client timeout. If FALSE, no timeouts
  410. are enforced (and this routine may block
  411. indefinitely).
  412. RETURNS: SOCKERR - 0 if successful, !0 if not.
  413. HISTORY:
  414. KeithMo 27-Apr-1993 Created.
  415. ********************************************************************/
  416. SOCKERR
  417. AcceptSocket(
  418. SOCKET sockListen,
  419. SOCKET * psockNew,
  420. LPSOCKADDR_IN paddr,
  421. BOOL fEnforceTimeout,
  422. FTP_SERVER_INSTANCE *pInstance
  423. )
  424. {
  425. SOCKERR serr = 0;
  426. SOCKET sockNew = INVALID_SOCKET;
  427. BOOL fRead = FALSE;
  428. DBG_ASSERT( psockNew != NULL );
  429. DBG_ASSERT( paddr != NULL );
  430. if( fEnforceTimeout ) {
  431. //
  432. // Timeouts are to be enforced, so wait for a connection
  433. // to the socket.
  434. //
  435. serr = WaitForSocketWorker(
  436. sockListen,
  437. INVALID_SOCKET,
  438. &fRead,
  439. NULL,
  440. pInstance->QueryConnectionTimeout()
  441. );
  442. }
  443. if( serr == 0 )
  444. {
  445. INT cbAddr = sizeof(SOCKADDR_IN);
  446. //
  447. // Wait for the actual connection.
  448. //
  449. sockNew = accept( sockListen, (SOCKADDR *)paddr, &cbAddr );
  450. if( sockNew == INVALID_SOCKET )
  451. {
  452. serr = WSAGetLastError();
  453. }
  454. }
  455. //
  456. // Return the (potentially invalid) socket to the caller.
  457. //
  458. *psockNew = sockNew;
  459. return serr;
  460. } // AcceptSocket
  461. /*******************************************************************
  462. NAME: SockSend
  463. SYNOPSIS: Sends a block of bytes to a specified socket.
  464. ENTRY: pUserData - The user initiating the request.
  465. sock - The target socket.
  466. pBuffer - Contains the data to send.
  467. cbBuffer - The size (in bytes) of the buffer.
  468. RETURNS: SOCKERR - 0 if successful, !0 if not.
  469. HISTORY:
  470. KeithMo 13-Mar-1993 Created.
  471. ********************************************************************/
  472. SOCKERR
  473. SockSend(
  474. LPUSER_DATA pUserData,
  475. SOCKET sock,
  476. LPVOID pBuffer,
  477. DWORD cbBuffer
  478. )
  479. {
  480. SOCKERR serr = 0;
  481. INT cbSent;
  482. DWORD dwBytesSent = 0;
  483. DBG_ASSERT( pBuffer != NULL );
  484. //
  485. // Loop until there's no more data to send.
  486. //
  487. while( cbBuffer > 0 ) {
  488. //
  489. // Wait for the socket to become writeable.
  490. //
  491. BOOL fWrite = FALSE;
  492. serr = WaitForSocketWorker(
  493. INVALID_SOCKET,
  494. sock,
  495. NULL,
  496. &fWrite,
  497. (pUserData != NULL) ?
  498. pUserData->QueryInstance()->QueryConnectionTimeout():
  499. FTP_DEF_SEND_TIMEOUT
  500. );
  501. if( serr == 0 )
  502. {
  503. //
  504. // Write a block to the socket.
  505. //
  506. cbSent = send( sock, (CHAR *)pBuffer, (INT)cbBuffer, 0 );
  507. if( cbSent < 0 )
  508. {
  509. //
  510. // Socket error.
  511. //
  512. serr = WSAGetLastError();
  513. }
  514. else
  515. {
  516. dwBytesSent += (DWORD)cbSent;
  517. IF_DEBUG( SEND )
  518. {
  519. if( pUserData && TEST_UF( pUserData, TRANSFER ) )
  520. {
  521. DBGPRINTF(( DBG_CONTEXT,
  522. "send %d bytes @%p to socket %d\n",
  523. cbSent,
  524. pBuffer,
  525. sock ));
  526. }
  527. }
  528. }
  529. }
  530. // added a check for special case when we are sending and thinking that we are sending
  531. // synchronoulsy on socket which was set to non blocking mode. In that case when buffer space
  532. // in winsock becomes exhausted send return WSAEWOULDBLOCK. So then we just retry.
  533. if ( serr != WSAEWOULDBLOCK )
  534. {
  535. if( serr != 0 )
  536. {
  537. break;
  538. }
  539. pBuffer = (LPVOID)( (LPBYTE)pBuffer + cbSent );
  540. cbBuffer -= (DWORD)cbSent;
  541. }
  542. }
  543. if( serr != 0 )
  544. {
  545. IF_DEBUG( SEND )
  546. {
  547. DBGPRINTF(( DBG_CONTEXT,
  548. "socket error %d during send on socket %d.\n",
  549. serr,
  550. sock));
  551. }
  552. }
  553. if( pUserData != NULL ) {
  554. pUserData->QueryInstance()->QueryStatsObj()->UpdateTotalBytesSent(
  555. dwBytesSent );
  556. }
  557. return serr;
  558. } // SockSend
  559. /*******************************************************************
  560. NAME: SockRecv
  561. SYNOPSIS: Receives a block of bytes from a specified socket.
  562. ENTRY: pUserData - The user initiating the request.
  563. sock - The target socket.
  564. pBuffer - Will receive the data.
  565. cbBuffer - The size (in bytes) of the buffer.
  566. pbReceived - Will receive the actual number of bytes
  567. received. This value is undefined if this function
  568. fails.
  569. RETURNS: SOCKERR - 0 if successful, !0 if not.
  570. HISTORY:
  571. KeithMo 13-Mar-1993 Created.
  572. ********************************************************************/
  573. SOCKERR
  574. SockRecv(
  575. LPUSER_DATA pUserData,
  576. SOCKET sock,
  577. LPVOID pBuffer,
  578. DWORD cbBuffer,
  579. LPDWORD pbReceived
  580. )
  581. {
  582. SOCKERR serr = 0;
  583. DWORD cbTotal = 0;
  584. INT cbReceived;
  585. DWORD dwBytesRecv = 0;
  586. DBG_ASSERT( pBuffer != NULL );
  587. DBG_ASSERT( pbReceived != NULL );
  588. //
  589. // Loop until the buffer's full.
  590. //
  591. while (cbBuffer > 0) {
  592. BOOL fRead = FALSE;
  593. //
  594. // Wait for the socket to become readable.
  595. //
  596. serr = WaitForSocketWorker(
  597. sock,
  598. INVALID_SOCKET,
  599. &fRead,
  600. NULL,
  601. (pUserData != NULL) ?
  602. pUserData->QueryInstance()->QueryConnectionTimeout():
  603. FTP_DEF_RECV_TIMEOUT
  604. );
  605. if( serr == 0 )
  606. {
  607. //
  608. // Read a block from the socket.
  609. //
  610. DBG_ASSERT( fRead);
  611. cbReceived = recv( sock, (CHAR *)pBuffer, (INT)cbBuffer, 0 );
  612. if( cbReceived < 0 )
  613. {
  614. //
  615. // Socket error.
  616. //
  617. serr = WSAGetLastError();
  618. }
  619. else
  620. {
  621. dwBytesRecv += (DWORD)cbReceived;
  622. IF_DEBUG( RECV )
  623. {
  624. if( pUserData && TEST_UF( pUserData, TRANSFER ) )
  625. {
  626. DBGPRINTF(( DBG_CONTEXT,
  627. "received %d bytes @%p from socket %d\n",
  628. cbReceived,
  629. pBuffer,
  630. sock ));
  631. }
  632. }
  633. }
  634. }
  635. if( ( serr != 0 ) || ( cbReceived == 0 ) )
  636. {
  637. //
  638. // End of file, socket closed, timeout, or socket error.
  639. //
  640. break;
  641. }
  642. pBuffer = (LPVOID)( (LPBYTE)pBuffer + cbReceived );
  643. cbBuffer -= (DWORD)cbReceived;
  644. cbTotal += (DWORD)cbReceived;
  645. }
  646. if( serr == 0 )
  647. {
  648. //
  649. // Return total byte count to caller.
  650. //
  651. *pbReceived = cbTotal;
  652. }
  653. else
  654. {
  655. IF_DEBUG( RECV )
  656. {
  657. DBGPRINTF(( DBG_CONTEXT,
  658. "socket error %d during recv on socket %d\n",
  659. serr,
  660. sock ));
  661. }
  662. }
  663. if( pUserData != NULL ) {
  664. pUserData->QueryInstance()->QueryStatsObj()->UpdateTotalBytesReceived(
  665. dwBytesRecv );
  666. }
  667. return serr;
  668. } // SockRecv
  669. /*******************************************************************
  670. NAME: SockPrintf2
  671. SYNOPSIS: Send a formatted string to a specific socket.
  672. ENTRY: pUserData - The user initiating the request.
  673. sock - The target socket.
  674. pszFormat - A printf-style format string.
  675. ... - Any other parameters needed by the format string.
  676. RETURNS: SOCKERR - 0 if successful, !0 if not.
  677. HISTORY:
  678. KeithMo 10-Mar-1993 Created.
  679. ********************************************************************/
  680. SOCKERR
  681. __cdecl
  682. SockPrintf2(
  683. LPUSER_DATA pUserData,
  684. SOCKET sock,
  685. LPCSTR pszFormat,
  686. ...
  687. )
  688. {
  689. va_list ArgPtr;
  690. SOCKERR serr;
  691. //
  692. // Let the worker do the dirty work.
  693. //
  694. va_start( ArgPtr, pszFormat );
  695. serr = vSockPrintf( pUserData,
  696. sock,
  697. pszFormat,
  698. ArgPtr );
  699. va_end( ArgPtr );
  700. return serr;
  701. } // SockPrintf2
  702. SOCKERR
  703. __cdecl
  704. ReplyToUser(
  705. IN LPUSER_DATA pUserData,
  706. IN UINT ReplyCode,
  707. IN LPCSTR pszFormat,
  708. ...
  709. )
  710. /*++
  711. This function sends an FTP reply to the user data object. The reply
  712. is usually sent over the control socket.
  713. Arguments:
  714. pUserData pointer to UserData object initiating the reply
  715. ReplyCode One of the REPLY_* manifests.
  716. pszFormat pointer to null-terminated string containing the format
  717. ... additional paramters if any required.
  718. Returns:
  719. SOCKET error code. 0 on success and !0 on failure.
  720. History:
  721. MuraliK
  722. --*/
  723. {
  724. va_list ArgPtr;
  725. SOCKERR serr;
  726. DBG_ASSERT( pUserData != NULL);
  727. pUserData->SetLastReplyCode( ReplyCode );
  728. if ( pUserData->QueryControlSocket() != INVALID_SOCKET) {
  729. //
  730. // Let the worker do the dirty work.
  731. //
  732. va_start( ArgPtr, pszFormat );
  733. serr = vSockReply( pUserData,
  734. pUserData->QueryControlSocket(),
  735. ReplyCode,
  736. ' ',
  737. pszFormat,
  738. ArgPtr );
  739. va_end( ArgPtr );
  740. } else {
  741. serr = WSAECONNABORTED;
  742. }
  743. return serr;
  744. } // ReplyToUser()
  745. // Private functions
  746. /*******************************************************************
  747. NAME: vSockPrintf
  748. SYNOPSIS: Worker function for printf-to-socket functions.
  749. ENTRY: pUserData - The user initiating the request.
  750. sock - The target socket.
  751. pszFormat - The format string.
  752. args - Variable number of arguments.
  753. RETURNS: SOCKERR - 0 if successful, !0 if not.
  754. HISTORY:
  755. KeithMo 17-Mar-1993 Created.
  756. ********************************************************************/
  757. SOCKERR
  758. vSockPrintf(
  759. LPUSER_DATA pUserData,
  760. SOCKET sock,
  761. LPCSTR pszFormat,
  762. va_list args
  763. )
  764. {
  765. INT cchBuffer = 0;
  766. INT buffMaxLen;
  767. SOCKERR serr = 0;
  768. CHAR szBuffer[MAX_REPLY_LENGTH];
  769. DBG_ASSERT( pszFormat != NULL );
  770. //
  771. // Render the format into our local buffer.
  772. //
  773. DBG_ASSERT( MAX_REPLY_LENGTH > 3);
  774. buffMaxLen = MAX_REPLY_LENGTH - 3;
  775. cchBuffer = _vsnprintf( szBuffer,
  776. buffMaxLen,
  777. pszFormat, args );
  778. //
  779. // The string length is long, we get back -1.
  780. // so we get the string length for partial data.
  781. //
  782. if ( cchBuffer == -1 ) {
  783. //
  784. // terminate the string properly,
  785. // since _vsnprintf() does not terminate properly on failure.
  786. //
  787. cchBuffer = buffMaxLen;
  788. szBuffer[ buffMaxLen] = '\0';
  789. }
  790. IF_DEBUG( SOCKETS ) {
  791. DBGPRINTF(( DBG_CONTEXT, "sending '%s'\n", szBuffer ));
  792. }
  793. //
  794. // Escape all telnet IAC bytes with a second IAC
  795. //
  796. vTelnetEscapeIAC( szBuffer, &cchBuffer, MAX_REPLY_LENGTH - 3 );
  797. strcpy( szBuffer + cchBuffer, "\r\n" );
  798. cchBuffer += 2;
  799. //
  800. // Blast it out to the client.
  801. //
  802. serr = SockSend( pUserData, sock, szBuffer, cchBuffer );
  803. return serr;
  804. } // vSockPrintf
  805. /*******************************************************************
  806. NAME: vSockReply
  807. SYNOPSIS: Worker function for reply functions.
  808. ENTRY: pUserData - The user initiating the request.
  809. sock - The target socket.
  810. ReplyCode - A three digit reply code from RFC 959.
  811. chSeparator - Should be either ' ' (normal reply) or
  812. '-' (first line of multi-line reply).
  813. pszFormat - The format string.
  814. args - Variable number of arguments.
  815. RETURNS: SOCKERR - 0 if successful, !0 if not.
  816. HISTORY:
  817. KeithMo 17-Mar-1993 Created.
  818. ********************************************************************/
  819. SOCKERR
  820. vSockReply(
  821. LPUSER_DATA pUserData,
  822. SOCKET sock,
  823. UINT ReplyCode,
  824. CHAR chSeparator,
  825. LPCSTR pszFormat,
  826. va_list args
  827. )
  828. {
  829. INT cchBuffer;
  830. INT cchBuffer1;
  831. INT buffMaxLen;
  832. SOCKERR serr = 0;
  833. CHAR szBuffer[MAX_REPLY_LENGTH];
  834. DBG_ASSERT( ( ReplyCode >= 100 ) && ( ReplyCode < 600 ) );
  835. //
  836. // Render the format into our local buffer.
  837. //
  838. cchBuffer = wsprintfA( szBuffer,
  839. "%u%c",
  840. ReplyCode,
  841. chSeparator );
  842. DBG_ASSERT( MAX_REPLY_LENGTH > cchBuffer + 3);
  843. buffMaxLen = MAX_REPLY_LENGTH - cchBuffer - 3;
  844. cchBuffer1 = _vsnprintf( szBuffer + cchBuffer,
  845. buffMaxLen,
  846. pszFormat, args );
  847. //
  848. // The string length is long, we get back -1.
  849. // so we get the string length for partial data.
  850. //
  851. if ( cchBuffer1 == -1 ) {
  852. //
  853. // terminate the string properly,
  854. // since _vsnprintf() does not terminate properly on failure.
  855. //
  856. cchBuffer = buffMaxLen;
  857. szBuffer[ buffMaxLen] = '\0';
  858. } else {
  859. cchBuffer += cchBuffer1;
  860. }
  861. IF_DEBUG( SOCKETS ) {
  862. DBGPRINTF(( DBG_CONTEXT, "sending '%s'\n",szBuffer ));
  863. }
  864. //
  865. // Escape all telnet IAC bytes with a second IAC
  866. //
  867. vTelnetEscapeIAC( szBuffer, &cchBuffer, MAX_REPLY_LENGTH - 3 );
  868. strcpy( szBuffer + cchBuffer, "\r\n" );
  869. cchBuffer += 2;
  870. //
  871. // Blast it out to the client.
  872. //
  873. serr = SockSend( pUserData, sock, szBuffer, cchBuffer );
  874. return serr;
  875. } // vSockReply
  876. DWORD
  877. FtpFormatResponseMessage( IN UINT uiReplyCode,
  878. IN LPCTSTR pszReplyMsg,
  879. OUT LPTSTR pszReplyBuffer,
  880. IN DWORD cchReplyBuffer)
  881. /*++
  882. This function formats the message to be sent to the client,
  883. given the reply code and the message to be sent.
  884. The formatting function takes care of the reply buffer length
  885. and ensures the safe write of data. If the reply buffer is
  886. not sufficient to hold the entire message, the reply msg is trunctaed.
  887. Arguments:
  888. uiReplyCode reply code to be used.
  889. pszReplyMsg pointer to string containing the reply message
  890. pszReplyBuffer pointer to character buffer where the reply message
  891. can be sent.
  892. cchReplyBuffer character count for the length of reply buffer.
  893. Returns:
  894. length of the data written to the reply buffer.
  895. --*/
  896. {
  897. DWORD len;
  898. DBG_ASSERT( pszReplyMsg != NULL && pszReplyBuffer != NULL);
  899. len = lstrlen( pszReplyMsg) + 10; // 10 chars are required for aux info.
  900. if ( len >= cchReplyBuffer) {
  901. // truncate the message since length is too high.
  902. len = wsprintf( pszReplyBuffer, TEXT("%u \r\n"),
  903. uiReplyCode);
  904. DBG_ASSERT( len >= 3); // length greater than formatting string
  905. DBG_ASSERT( len < cchReplyBuffer);
  906. lstrcpyn( pszReplyBuffer + len, pszReplyMsg, cchReplyBuffer - len);
  907. len = lstrlen( pszReplyBuffer);
  908. DBG_ASSERT( len < cchReplyBuffer);
  909. } else {
  910. len = wsprintf( pszReplyBuffer, TEXT("%u %s\r\n"),
  911. uiReplyCode,
  912. pszReplyMsg);
  913. DBG_ASSERT( len >= 4);
  914. DBG_ASSERT( len <= cchReplyBuffer);
  915. }
  916. return (len);
  917. } // FtpFormatResponseMessage()
  918. /*******************************************************************
  919. NAME: vTelnetEscapeIAC
  920. SYNOPSIS: replace (in place) all 0xFF bytes in a buffer with 0xFF 0xFF.
  921. This is the TELNET escape sequence for an IAC value data byte.
  922. ENTRY: pszBuffer - data buffer.
  923. pcchBufChars - on entry, current number of chars in buffer.
  924. on return, number of chars in buffer.
  925. cchMaxLen - maximum characters in the output buffer
  926. RETURNS: TRUE - success, FALSE - overflow.
  927. HISTORY:
  928. RobSol 25-April-2001 Created.
  929. ********************************************************************/
  930. BOOL
  931. vTelnetEscapeIAC( IN OUT PCHAR pszBuffer,
  932. IN OUT PINT pcchBufChars,
  933. IN INT cchMaxLen)
  934. {
  935. # define CHAR_IAC ((CHAR)(-1))
  936. PCHAR pszFirstIAC;
  937. PCHAR pSrc, pDst;
  938. BOOL fReturn = TRUE;
  939. CHAR szBuf[MAX_REPLY_LENGTH];
  940. INT cCharsInDst;
  941. DBG_ASSERT( pszBuffer );
  942. DBG_ASSERT( pcchBufChars );
  943. DBG_ASSERT( cchMaxLen <= MAX_REPLY_LENGTH );
  944. if ((pszFirstIAC = strchr( pszBuffer, CHAR_IAC)) == NULL) {
  945. //
  946. // no IAC - return.
  947. //
  948. return TRUE;
  949. }
  950. //
  951. // we'll expand the string into a temp buffer, then copy back.
  952. //
  953. pSrc = pszFirstIAC;
  954. pDst = szBuf;
  955. cCharsInDst = DIFF(pszFirstIAC - pszBuffer);
  956. do {
  957. if (*pSrc == CHAR_IAC) {
  958. //
  959. // this is a char to escape
  960. //
  961. if ((cCharsInDst + 1) < cchMaxLen) {
  962. //
  963. // we have space to escape the char, so do it.
  964. //
  965. cCharsInDst++;
  966. *pDst++ = CHAR_IAC;
  967. } else {
  968. //
  969. // overflow.
  970. //
  971. fReturn = FALSE;
  972. break;
  973. }
  974. }
  975. *pDst++ = *pSrc++;
  976. } while ((++cCharsInDst < cchMaxLen) && (*pSrc != '\0'));
  977. //
  978. // copy the expanded data back into the input buffer and terminate the string
  979. //
  980. memcpy( pszFirstIAC, szBuf, pDst - szBuf);
  981. pszBuffer[ cCharsInDst ] = '\0';
  982. *pcchBufChars = cCharsInDst;
  983. return fReturn;
  984. }
  985. /************************ End of File ******************************/