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.

690 lines
16 KiB

  1. /*++
  2. Copyright (c) 1995 Microsoft Corporation
  3. Module Name :
  4. ftps.cxx
  5. Abstract:
  6. This file defines the ftp server stress client
  7. Author:
  8. Murali R. Krishnan ( MuraliK ) 25-July-1995
  9. Environment:
  10. Win32 - uses Wininet extensions
  11. Project:
  12. FTP Server DLL
  13. Functions Exported:
  14. Revision History:
  15. MuraliK 05-Nov-1995 Added support for GetFile tests
  16. --*/
  17. /************************************************************
  18. * Include Headers
  19. ************************************************************/
  20. # include <windows.h>
  21. # include <wininet.h>
  22. # include <stdio.h>
  23. # include <stdlib.h>
  24. # include <iostream.h>
  25. # include <winsock2.h>
  26. #define DEFAULT_TRACE_FLAGS (DEBUG_ITERATION)
  27. # include <dbgutil.h>
  28. # define DEFAULT_NUMBER_OF_THREADS (10)
  29. # define DEFAULT_NUMBER_OF_ITERATIONS (100)
  30. # define MAX_BUFFER_SIZE 64000 // 64K approx
  31. #ifndef _NO_TRACING_
  32. #include <initguid.h>
  33. DEFINE_GUID(IisFtptestGuid,
  34. 0x784d8914, 0xaa8c, 0x11d2, 0x92, 0x5e, 0x00, 0xc0, 0x4f, 0x72, 0xd9, 0x0e);
  35. #else
  36. DECLARE_DEBUG_VARIABLE();
  37. #endif
  38. DECLARE_DEBUG_PRINTS_OBJECT();
  39. static const char PSZ_APPLICATION_NAME[] = "murali's stresser";
  40. static char * g_lpszServerAddress;
  41. BOOL
  42. GenUsageMessage( int argc, char * argv[]);
  43. // Tests the raw connectivity to server.
  44. BOOL TestConnections( int argc, char * argv[]);
  45. // Tests the raw get file from server.
  46. BOOL TestGetFile( int argc, char * argv[]);
  47. //
  48. // The following DefineAllCommands() defines a template for all commands.
  49. // Format: CmdCodeName CommandName Function Pointer Comments
  50. //
  51. // To add addditional test commands, add just another line to the list
  52. // Dont touch any macros below, they are all automatically generated.
  53. // Always the first entry should be usage function.
  54. //
  55. #define DefineAllCommands() \
  56. Cmd( CmdUsage, "usage", GenUsageMessage, \
  57. " Commands Available" ) \
  58. Cmd( CmdConnections, "conn", TestConnections, \
  59. " Raw Connections to server" ) \
  60. Cmd( CmdGetFile, "get", TestGetFile, \
  61. " Simple Get File (/readme.txt) from server" ) \
  62. // Define command codes
  63. # define Cmd( CmdCode, CmdName, CmdFunc, CmdComments) CmdCode,
  64. typedef enum _CmdCodes {
  65. DefineAllCommands()
  66. maxCmdCode
  67. } CmdCodes;
  68. #undef Cmd
  69. // Define the functions and array of mappings
  70. // General command function type
  71. typedef BOOL ( * CMDFUNC)( int argc, char * argv[]);
  72. typedef struct _CmdStruct {
  73. CmdCodes cmdCode;
  74. char * pszCmdName;
  75. CMDFUNC cmdFunc;
  76. char * pszCmdComments;
  77. } CmdStruct;
  78. // Define Prototypes of command functions
  79. # define Cmd( CmdCode, CmdName, CmdFunc, CmdComments) \
  80. BOOL CmdFunc(int argc, char * argv[]);
  81. // Cause an expansion to generate prototypes
  82. // DefineAllCommands()
  83. // Automatic generation causes a problem when we have NULL in Function ptrs :(
  84. // Let the user explicitly define the prototypes
  85. #undef Cmd
  86. //
  87. // Define the global array of commands
  88. //
  89. # define Cmd( CmdCode, CmdName, CmdFunc, CmdComments) \
  90. { CmdCode, CmdName, CmdFunc, CmdComments},
  91. static CmdStruct g_cmds[] = {
  92. DefineAllCommands()
  93. { maxCmdCode, NULL, NULL} // sentinel command
  94. };
  95. #undef Cmd
  96. /************************************************************
  97. * Functions
  98. ************************************************************/
  99. BOOL
  100. GenUsageMessage( int argc, char * argv[])
  101. {
  102. CmdStruct * pCmd;
  103. printf( " Usage:\n %s <server-name/address> <cmd name> <cmd arguments>\n",
  104. argv[0]);
  105. for( pCmd = g_cmds; pCmd != NULL && pCmd->cmdCode != maxCmdCode; pCmd++) {
  106. printf( "\t%s\t%s\n", pCmd->pszCmdName, pCmd->pszCmdComments);
  107. }
  108. return ( TRUE);
  109. } // GenUsageMessage()
  110. static
  111. CmdStruct * DecodeCommand( char * pszCmd)
  112. {
  113. CmdStruct * pCmd;
  114. if ( pszCmd != NULL) {
  115. for( pCmd = g_cmds;
  116. pCmd != NULL && pCmd->cmdCode != maxCmdCode; pCmd++) {
  117. if ( _stricmp( pszCmd, pCmd->pszCmdName) == 0) {
  118. return ( pCmd);
  119. }
  120. } // for
  121. }
  122. return ( &g_cmds[0]); // No match found, return usage message
  123. } // DecodeCommand()
  124. /************************************************************
  125. * Functions
  126. ************************************************************/
  127. inline
  128. VOID
  129. GenUserName( OUT LPSTR pszBuffer, IN LPCSTR pszPrefix, IN DWORD iteration)
  130. {
  131. // assume sufficient buffer space
  132. // Format of response: [email protected]
  133. sprintf( pszBuffer, "%s@p%d.t%d.%d",
  134. pszPrefix,
  135. GetCurrentProcessId(),
  136. GetCurrentThreadId(),
  137. iteration);
  138. return;
  139. } // GenUserName()
  140. DWORD
  141. TestConnectionsInOneThread( IN CHAR * pszServer, IN DWORD nIterations)
  142. {
  143. HINTERNET hinet;
  144. HINTERNET hFtp;
  145. DWORD NumSuccess = 0;
  146. CHAR rgchBuffer[300];
  147. CHAR rgchUserName[100];
  148. DWORD cbBuffer;
  149. DWORD dwError;
  150. DWORD iter;
  151. BOOL fReturn2;
  152. hinet = InternetOpen( PSZ_APPLICATION_NAME, 0, NULL, 0, 0);
  153. IF_DEBUG( ENTRY) {
  154. DBGPRINTF(( DBG_CONTEXT, "InternetOpen()==> %08x\n", hinet));
  155. }
  156. if ( hinet == NULL) {
  157. return (0);
  158. }
  159. for( iter = 0 ; iter < nIterations; iter++) {
  160. IF_DEBUG( ITERATION) {
  161. DBGPRINTF(( DBG_CONTEXT, "Iteration = %u\n", iter));
  162. }
  163. GenUserName( rgchUserName, "conn", iter);
  164. hFtp = InternetConnect( hinet, pszServer, 0, "anonymous",
  165. rgchUserName,
  166. INTERNET_SERVICE_FTP, 0, NULL);
  167. IF_DEBUG( ENTRY) {
  168. DBGPRINTF(( DBG_CONTEXT, " InternetConnect()==> %08x\n",
  169. hFtp));
  170. }
  171. if ( hFtp == NULL) {
  172. continue;
  173. }
  174. IF_DEBUG( ENTRY) {
  175. DBGPRINTF(( DBG_CONTEXT, " InternetGetLastResponse()\n"));
  176. }
  177. cbBuffer = sizeof(rgchBuffer) - 1;
  178. if ( InternetGetLastResponseInfo( &dwError, rgchBuffer, &cbBuffer)) {
  179. IF_DEBUG( RESPONSE) {
  180. rgchBuffer[cbBuffer] = '\0';
  181. cout << " ErrorCode = " << dwError
  182. << "\tResponse = " << rgchBuffer;
  183. }
  184. }
  185. IF_DEBUG( ENTRY) {
  186. DBGPRINTF(( DBG_CONTEXT, " InternetCloseHandle(%08x)\n",
  187. hFtp));
  188. }
  189. if ( InternetCloseHandle(hFtp)) {
  190. NumSuccess++;
  191. }
  192. } // for()
  193. IF_DEBUG( ENTRY) {
  194. DBGPRINTF(( DBG_CONTEXT, "InternetCloseHandle(%08x)\n",
  195. hinet));
  196. }
  197. fReturn2 = InternetCloseHandle(hinet);
  198. return ( NumSuccess);
  199. } // TestConnectionsInOneThread()
  200. BOOL TestConnections( int argc, char * argv[])
  201. /*++
  202. This function routinely establishes and throws away connections.
  203. It does not do any other work.
  204. This is used for testing logon and quit sequences of FTP server.
  205. Arguments:
  206. argc count of arguments
  207. argv arguments
  208. argv[0] = "conn" -- name of this test function
  209. argv[1] = # of thread to use for execution
  210. argv[2] = # of iterations for each thread.
  211. --*/
  212. {
  213. DWORD NumThreads = DEFAULT_NUMBER_OF_THREADS;
  214. DWORD NumIterations = DEFAULT_NUMBER_OF_ITERATIONS;
  215. DWORD NumSuccesses;
  216. if (argc >= 2 && argv[1] != NULL) {
  217. NumThreads = atoi(argv[1]);
  218. }
  219. if (argc >= 3 && argv[2] != NULL) {
  220. NumIterations = atoi(argv[2]);
  221. }
  222. // We will implement multithreading later on.
  223. // for now just one thread is used. ( current thread)
  224. NumSuccesses = TestConnectionsInOneThread(g_lpszServerAddress,
  225. NumIterations);
  226. cout << " Tested for Iterations : " << NumIterations;
  227. cout << " Successes = " << NumSuccesses;
  228. cout << " Failures = " << (NumIterations - NumSuccesses) << endl;
  229. return ( NumSuccesses == NumIterations);
  230. } // TestConnections()
  231. DWORD
  232. ReadFtpFile( IN HINTERNET hFtp, IN LPCSTR pszFileName, IN DWORD sTimeout)
  233. {
  234. HINTERNET hFtpFile;
  235. CHAR chBuffer[MAX_BUFFER_SIZE];
  236. DWORD dwBytesRead = 0;
  237. DWORD dwError = NO_ERROR;
  238. BOOL fRead;
  239. if ( hFtp == NULL) {
  240. return ( ERROR_INVALID_PARAMETER);
  241. }
  242. hFtpFile = FtpOpenFile( hFtp, pszFileName, GENERIC_READ,
  243. FTP_TRANSFER_TYPE_ASCII, 0);
  244. IF_DEBUG( ENTRY) {
  245. DBGPRINTF(( DBG_CONTEXT, "\t\tFtpOpenFile(%s) ==> %08x\n",
  246. pszFileName, hFtpFile));
  247. }
  248. if ( hFtpFile == NULL) {
  249. return ( GetLastError());
  250. }
  251. //===================================
  252. //
  253. // Start copying the file from the
  254. // server to the client
  255. //
  256. //====================================
  257. if ( sTimeout != 0) {
  258. //
  259. // sleep causes data time out to occur.
  260. // If there is a non-zero timeout then we will have
  261. // problems with data sockets timing out on server.
  262. //
  263. cout << " Requested " << pszFileName <<
  264. " Sleeping for " << sTimeout << " seconds" << endl;
  265. Sleep( sTimeout * 1000);
  266. }
  267. do {
  268. dwError = NO_ERROR;
  269. // read and discard the data.
  270. fRead = InternetReadFile(
  271. hFtpFile,
  272. (LPVOID) chBuffer,
  273. (DWORD) MAX_BUFFER_SIZE,
  274. (LPDWORD) &dwBytesRead);
  275. if ( !fRead) {
  276. dwError = GetLastError();
  277. DBGPRINTF(( DBG_CONTEXT, " InternetReadFile(%s) failed."
  278. " Error=%d\n",
  279. pszFileName, dwError));
  280. }
  281. IF_DEBUG( ENTRY) {
  282. DBGPRINTF(( DBG_CONTEXT, " InternetReadFile(%08x, %s) ==> %d"
  283. " (Err=%d)\n",
  284. hFtpFile, pszFileName, fRead, dwError));
  285. }
  286. } while (fRead && dwBytesRead > 0);
  287. //
  288. // Close the handle for read file
  289. //
  290. if (!InternetCloseHandle(hFtpFile)) {
  291. if ( dwError != NO_ERROR) {
  292. DBGERROR(( DBG_CONTEXT, " Double Errors are occuring Old=%d\n",
  293. dwError));
  294. }
  295. dwError = ( GetLastError());
  296. }
  297. return ( dwError);
  298. } // ReadFtpFile()
  299. DWORD
  300. TestGetFileInOneThread(IN CHAR * pszServer,
  301. IN LPCSTR pszFileName,
  302. IN DWORD nIterations,
  303. IN DWORD sTimeout)
  304. {
  305. HINTERNET hinet;
  306. HINTERNET hFtp;
  307. DWORD NumSuccess = 0;
  308. CHAR rgchBuffer[300];
  309. CHAR rgchUserName[100];
  310. DWORD cbBuffer;
  311. DWORD dwError;
  312. DWORD iter;
  313. BOOL fReturn2;
  314. hinet = InternetOpen( PSZ_APPLICATION_NAME, 0, NULL, 0, 0);
  315. IF_DEBUG( ENTRY) {
  316. DBGPRINTF(( DBG_CONTEXT, "InternetOpen()==> %08x\n", hinet));
  317. }
  318. if ( hinet == NULL) {
  319. return (0);
  320. }
  321. for( iter = 0 ; iter < nIterations; iter++) {
  322. DWORD dwReadError = NO_ERROR;
  323. IF_DEBUG( ITERATION) {
  324. DBGPRINTF(( DBG_CONTEXT, "Iteration = %u\n", iter));
  325. }
  326. GenUserName( rgchUserName, "getFile", iter);
  327. hFtp = InternetConnect( hinet, pszServer, 0, "anonymous", rgchUserName,
  328. INTERNET_SERVICE_FTP, 0, NULL);
  329. IF_DEBUG( ENTRY) {
  330. DBGPRINTF(( DBG_CONTEXT, " InternetConnect()==> %08x\n",
  331. hFtp));
  332. }
  333. if ( hFtp == NULL) {
  334. continue;
  335. }
  336. dwReadError = ReadFtpFile( hFtp, pszFileName, sTimeout);
  337. if ( dwReadError != NO_ERROR) {
  338. cout << " Read File failed: Error = " << dwReadError << endl;
  339. }
  340. IF_DEBUG( ENTRY) {
  341. DBGPRINTF(( DBG_CONTEXT, " InternetGetLastResponse()\n"));
  342. }
  343. cbBuffer = sizeof(rgchBuffer) - 1;
  344. if ( InternetGetLastResponseInfo( &dwError, rgchBuffer, &cbBuffer)) {
  345. IF_DEBUG( RESPONSE) {
  346. rgchBuffer[cbBuffer] = '\0';
  347. cout << " ErrorCode = " << dwError
  348. << "\tResponse = " << rgchBuffer << endl;
  349. }
  350. }
  351. IF_DEBUG( ENTRY) {
  352. DBGPRINTF(( DBG_CONTEXT, " InternetCloseHandle(%08x)\n",
  353. hFtp));
  354. }
  355. if ( InternetCloseHandle(hFtp) && dwReadError == NO_ERROR) {
  356. NumSuccess++;
  357. }
  358. } // for()
  359. IF_DEBUG( ENTRY) {
  360. DBGPRINTF(( DBG_CONTEXT, "InternetCloseHandle(%08x)\n",
  361. hinet));
  362. }
  363. fReturn2 = InternetCloseHandle(hinet);
  364. return ( NumSuccess);
  365. } // TestGetFileInOneThread()
  366. BOOL TestGetFile( int argc, char * argv[])
  367. /*++
  368. This function routinely establishes and throws away connections.
  369. It does not do any other work.
  370. This is used for testing logon and quit sequences of FTP server.
  371. Arguments:
  372. argc count of arguments
  373. argv arguments
  374. argv[0] = "get" -- name of this test function
  375. argv[1] = # of thread to use for execution
  376. argv[2] = # of iterations for each thread.
  377. argv[3] = Name of file/path to get from server.
  378. (default = /readme.txt)
  379. argv[4] = Sleep time for enabling test of data transfer timeouts
  380. Units = seconds
  381. ( default = 0 ==> do not sleep).
  382. --*/
  383. {
  384. DWORD NumThreads = DEFAULT_NUMBER_OF_THREADS;
  385. DWORD NumIterations = DEFAULT_NUMBER_OF_ITERATIONS;
  386. DWORD NumSuccesses;
  387. LPSTR pszFileToGet = "/readme.txt";
  388. DWORD sTimeout = 0;
  389. int i;
  390. i = argc; // "i" is the running counter.
  391. switch ( argc) {
  392. case 5:
  393. --i;
  394. DBG_ASSERT( argv[i] != NULL);
  395. sTimeout = atoi(argv[i]);
  396. // Fall Through
  397. case 4:
  398. --i;
  399. DBG_ASSERT( argv[i] != NULL);
  400. pszFileToGet = argv[i];
  401. // Fall Through
  402. case 3:
  403. --i;
  404. DBG_ASSERT( argv[i] != NULL);
  405. NumIterations = atoi(argv[i]);
  406. // Fall through
  407. case 2:
  408. --i;
  409. DBG_ASSERT( argv[i] != NULL);
  410. NumThreads = atoi( argv[i]);
  411. // Fall through
  412. default:
  413. case 1: case 0:
  414. break;
  415. } // switch
  416. // We will implement multithreading later on.
  417. // for now just one thread is used. ( current thread)
  418. NumSuccesses = TestGetFileInOneThread(g_lpszServerAddress,
  419. pszFileToGet,
  420. NumIterations,
  421. sTimeout);
  422. cout << " Tested GetFile for Iterations: " << NumIterations << endl;
  423. cout << " Timeout for Sleep = " << sTimeout << " seconds" << endl;
  424. cout << " Successes = " << NumSuccesses;
  425. cout << " Failures = " << (NumIterations - NumSuccesses) << endl;
  426. return ( NumSuccesses == NumIterations);
  427. } // TestGetFile()
  428. int __cdecl
  429. main( int argc, char * argv[])
  430. {
  431. DWORD err = NO_ERROR;
  432. char ** ppszArgv; // arguments for command functions
  433. int cArgs; // arg count for command functions
  434. char * pszCmdName;
  435. CmdStruct * pCmd;
  436. CMDFUNC pCmdFunc = NULL;
  437. #ifndef _NO_TRACING_
  438. CREATE_DEBUG_PRINT_OBJECT( "ftpstress", IisFtptestGuid);
  439. CREATE_INITIALIZE_DEBUG();
  440. #else
  441. CREATE_DEBUG_PRINT_OBJECT( "ftpstress");
  442. SET_DEBUG_FLAGS( DEBUG_ITERATION);
  443. #endif
  444. if ( argc < 3 || argv[1] == NULL ) {
  445. // Insufficient arguments
  446. GenUsageMessage( argc, argv);
  447. return ( 1);
  448. }
  449. pszCmdName = argv[2];
  450. if (( pCmd = DecodeCommand( pszCmdName)) == NULL
  451. || pCmd->cmdFunc == NULL) {
  452. printf( "Internal Error: Invalid Command %s\n", pszCmdName);
  453. GenUsageMessage( argc, argv);
  454. return ( 1);
  455. }
  456. g_lpszServerAddress = argv[1]; // get server address
  457. cArgs = argc - 2;
  458. ppszArgv = argv + 2;
  459. if ( !(*pCmd->cmdFunc)( cArgs, ppszArgv)) { // call the test function
  460. // Test function failed.
  461. printf( "Command %s failed. Error = %d\n", pszCmdName, GetLastError());
  462. return ( 1);
  463. }
  464. printf( " Command %s succeeded\n", pszCmdName);
  465. DELETE_DEBUG_PRINT_OBJECT();
  466. return ( 0); // success
  467. } // main()
  468. /************************ End of File ***********************/