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.

665 lines
22 KiB

  1. #line 1 "manyicmp.c"
  2. //================================================================================
  3. // Microsoft Confidential
  4. // Copyright (C) Microsoft Corporation 1997
  5. //
  6. // Author: RameshV
  7. //================================================================================
  8. //================================================================================
  9. // Required headers
  10. //================================================================================
  11. #include "inc.h"
  12. #pragma hdrstop
  13. #include <ipexport.h>
  14. #include <icmpif.h>
  15. #include <icmpapi.h>
  16. #include <winsock.h>
  17. #include <stdio.h>
  18. #include <stdlib.h>
  19. #include <align.h>
  20. #include <adt.h>
  21. #include "adt.c"
  22. //================================================================================
  23. // Required function, IMPORTED
  24. //================================================================================
  25. VOID
  26. HandleIcmpResult( // Handle a processed ICMP packet.
  27. IPAddr DestAddr, // Attempted dest address
  28. BOOL Status, // Non-zero=> Dest reachable
  29. LPVOID Context // Whatever was passed to DoIcmpRe..
  30. );
  31. //================================================================================
  32. // Functions EXPORTED
  33. //================================================================================
  34. DWORD // Win32 errors
  35. DoIcmpRequest( // send icmp req. and process asynchro..
  36. IPAddr DestAddr, // Address to send ping to
  37. LPVOID Context // the parameter to above function..
  38. );
  39. DWORD // Win32 errors
  40. PingInit( // Initialize all globals..
  41. VOID
  42. );
  43. VOID
  44. PingCleanup( // Free memory and close handles..
  45. VOID
  46. );
  47. //================================================================================
  48. // Some defines
  49. //================================================================================
  50. #define PING_TEST // Yes, we are testing PING.
  51. #define WAIT_TIME 5000
  52. #define RCV_BUF_SIZE 0x2000
  53. #define SEND_MESSAGE "IcmpDhcpTest"
  54. #define THREAD_KILL_TIME INFINITE
  55. #define MAX_PENDING_REQUESTS 100
  56. #define NUM_RETRIES 3
  57. #if DBG
  58. #define DhcpAssert(Condition) do{ \
  59. if(!(Condition)) AssertFailed(#Condition, __FILE__, __LINE__); } \
  60. while(0)
  61. #define ErrorPrint printf
  62. #else
  63. #define DhcpAssert(Condition)
  64. #define ErrorPrint (void)
  65. #endif
  66. //================================================================================
  67. // Data structures NOT EXPORTED
  68. //================================================================================
  69. // The follwing is the structure that is passed back to the callback function
  70. typedef struct st_apcContext { // struct passed to APC routine
  71. LIST_ENTRY IcmpRepliesList; // the chain of replies got is stored here
  72. LIST_ENTRY IcmpRequestsList; // The list that holds the icmp response
  73. PICMP_ECHO_REPLY Reply; // Icmp reply packet
  74. DWORD ReplySize; // The size of above buffer.
  75. IPAddr DestinationAddress; // Who are we try to ping?
  76. DWORD Status; // Did we succeed? Also retry count.
  77. LPVOID Context; // Dont know what this is goint to be
  78. } APC_CONTEXT, *PAPC_CONTEXT;
  79. // All globals are here.
  80. LIST_ENTRY IcmpRepliesList; // Here is where the IcmpReplies are spooled
  81. LIST_ENTRY IcmpRequestsList; // Here is where the
  82. CRITICAL_SECTION IcmpRepliesCritSect; // To access the replies list
  83. CRITICAL_SECTION IcmpRequestsCritSect; // To access the requests list
  84. HANDLE IcmpRepliesEvent; // Signaled each time a reply is received
  85. HANDLE IcmpRequestsEvent; // Singaled whenever a request is received
  86. HANDLE TerminateEvent; // Quit everything being done.
  87. CRITICAL_SECTION OutputCritSect; // To co-ordinate access to console output
  88. HANDLE RequestsThreadHandle; // The handle of the thread that takes requests
  89. HANDLE RepliesThreadHandle; // The handle of the thread that takes replies
  90. HANDLE IcmpHandle; // Handle to do IcmpSendEcho2 etc.
  91. PRODCONS IcmpProdConsSynchObj; // Producer/Consumer synchro.. object
  92. BOOL Terminating = FALSE; // Are we terminating?
  93. #define LOCK_REPLIES_LIST() EnterCriticalSection(&IcmpRepliesCritSect)
  94. #define LOCK_REQUESTS_LIST() EnterCriticalSection(&IcmpRequestsCritSect)
  95. #define LOCK_OUTPUT() EnterCriticalSection(&OutputCritSect)
  96. #define UNLOCK_REPLIES_LIST() LeaveCriticalSection(&IcmpRepliesCritSect)
  97. #define UNLOCK_REQUESTS_LIST() LeaveCriticalSection(&IcmpRequestsCritSect)
  98. #define UNLOCK_OUTPUT() LeaveCriticalSection(&OutputCritSect)
  99. //================================================================================
  100. // Assertion failure routine + allocation free routines
  101. //================================================================================
  102. VOID static _inline
  103. AssertFailed(
  104. LPSTR Condition,
  105. LPSTR File,
  106. DWORD Line
  107. ) {
  108. BYTE Buf[1000];
  109. DWORD RetVal;
  110. sprintf(Buf, "[%s:%d] %s", File, Line, Condition);
  111. RetVal = MessageBox(
  112. NULL, // hWnd: NULL => default desktop
  113. Buf, // Text to print
  114. "Assertion Failure", // Window caption
  115. MB_OK | MB_ICONSTOP // Message type
  116. );
  117. }
  118. LPVOID _inline
  119. DhcpAllocateMemory(
  120. DWORD Size
  121. ) {
  122. return LocalAlloc(LMEM_FIXED, Size);
  123. }
  124. VOID _inline
  125. DhcpFreeMemory(
  126. LPVOID Ptr
  127. ) {
  128. LocalFree(Ptr);
  129. }
  130. //================================================================================
  131. // Routines
  132. //================================================================================
  133. //--------------------------------------------------------------------------------
  134. // The following functions are on the replies side. They handle the icmp reply
  135. // packet and take the necessary action depending on the status, etc.
  136. //--------------------------------------------------------------------------------
  137. VOID static
  138. ApcRoutine( // This is called when ping completes
  139. IN PVOID Context, // The above structure
  140. IN PIO_STATUS_BLOCK Ignored1, // Unused param
  141. IN ULONG Ignored2 // Unused param
  142. ) {
  143. BOOL Status;
  144. PAPC_CONTEXT ApcContext = (PAPC_CONTEXT)Context;
  145. if( TRUE == Terminating ) {
  146. // Just free memory and quit?
  147. ASSERT( FALSE );
  148. DhcpFreeMemory(ApcContext);
  149. return;
  150. }
  151. // All we have to do is add it to the Replies List and signal the event
  152. LOCK_REPLIES_LIST();
  153. InsertTailList(&IcmpRepliesList, &ApcContext->IcmpRepliesList);
  154. UNLOCK_REPLIES_LIST();
  155. Status = SetEvent(IcmpRepliesEvent);
  156. ASSERT( FALSE != Status );
  157. }
  158. // The following function decides if the Destination is reachable or not.
  159. BOOL static
  160. DestReachable( // Is destination reachable?
  161. IN PAPC_CONTEXT ApcContext // this has the info of sender etc..
  162. ) {
  163. DWORD nReplies, i;
  164. // First parse the packet.
  165. nReplies = IcmpParseReplies(ApcContext->Reply, ApcContext->ReplySize);
  166. // if no replies, then straight assume that destination is unreachable.
  167. if( 0 == nReplies ) {
  168. // If there was no reply, there is no way for us to reach this
  169. // So, we assume that the Dest is NOT reachable.
  170. // Reasons could be IP_REQ_TIMED_OUT or IP_BAD_DESTINATION etc..
  171. return FALSE;
  172. }
  173. // Now we check each reply to see if there is anything from the same dest
  174. // address we are looking for. And if the status is success. If the status
  175. // is not success, we actually do not check anything there. Potentially, it
  176. // could be IP_DEST_PORT_UNREACHABLE, in which case, the dest machine is up,
  177. // but for some reason we tried the wrong port..
  178. for( i = 0; i < nReplies; i ++ ) {
  179. if( ApcContext->DestinationAddress == ApcContext->Reply[i].Address ) {
  180. // hit the destination!
  181. ASSERT( IP_SUCCESS == ApcContext->Reply[i].Status );
  182. return TRUE;
  183. }
  184. ASSERT( IP_SUCCESS != ApcContext->Reply[i].Status);
  185. }
  186. // None of the replies were successful.
  187. return FALSE;
  188. }
  189. VOID static
  190. HandleRepliesEvent( // Process all replies received
  191. VOID
  192. ) {
  193. PAPC_CONTEXT ApcContext;
  194. PLIST_ENTRY listEntry;
  195. BOOL Status;
  196. LOCK_REPLIES_LIST();
  197. while( !IsListEmpty( &IcmpRepliesList ) ) {
  198. // retrive the first element in the list
  199. ApcContext = CONTAINING_RECORD(IcmpRepliesList.Flink, APC_CONTEXT, IcmpRepliesList);
  200. RemoveEntryList(&ApcContext->IcmpRepliesList);
  201. UNLOCK_REPLIES_LIST();
  202. Status = DestReachable(ApcContext);
  203. if( Status || NUM_RETRIES <= ApcContext->Status ) {
  204. StartConsumer(&IcmpProdConsSynchObj);
  205. HandleIcmpResult(
  206. ApcContext->DestinationAddress,
  207. Status,
  208. ApcContext->Context
  209. );
  210. DhcpFreeMemory(ApcContext);
  211. EndConsumer(&IcmpProdConsSynchObj);
  212. } else {
  213. // retry
  214. LOCK_REQUESTS_LIST();
  215. InsertTailList(&IcmpRequestsList, &ApcContext->IcmpRequestsList);
  216. UNLOCK_REQUESTS_LIST();
  217. Status = SetEvent(IcmpRequestsEvent);
  218. ASSERT( TRUE == Status );
  219. }
  220. LOCK_REPLIES_LIST();
  221. }
  222. UNLOCK_REPLIES_LIST();
  223. }
  224. // This routine sleeps on a loop, and is woken up by the call back function when an ICMP
  225. // reply comes through. On waking up, this routine processes ALL ICMP replies.
  226. DWORD static // THREAD ENTRY
  227. LoopOnIcmpReplies( // Loop on all the ICMP replies.
  228. LPVOID Unused
  229. ) {
  230. DWORD Status;
  231. HANDLE WaitHandles[2];
  232. WaitHandles[0] = TerminateEvent;
  233. WaitHandles[1] = IcmpRepliesEvent;
  234. while( TRUE ) {
  235. Status = WaitForMultipleObjects(
  236. sizeof(WaitHandles)/sizeof(WaitHandles[0]),
  237. WaitHandles,
  238. FALSE,
  239. INFINITE
  240. );
  241. if( WAIT_OBJECT_0 == Status ) break; // Termination
  242. if( 1+WAIT_OBJECT_0 == Status ) {
  243. HandleRepliesEvent();
  244. continue;
  245. }
  246. ASSERT( FALSE );
  247. }
  248. return ERROR_SUCCESS;
  249. }
  250. #define AlignSizeof(X) ROUND_UP_COUNT(sizeof(X),ALIGN_WORST)
  251. DWORD // exported
  252. DoIcmpRequest(
  253. IPAddr DestAddr,
  254. LPVOID Context
  255. ) {
  256. PAPC_CONTEXT pCtxt;
  257. LPBYTE startAddress;
  258. DWORD Status;
  259. BOOL BoolStatus;
  260. // Create the context
  261. pCtxt = DhcpAllocateMemory(AlignSizeof(APC_CONTEXT) + RCV_BUF_SIZE);
  262. startAddress = (LPBYTE)pCtxt;
  263. if( NULL == pCtxt ) return GetLastError();
  264. // Now fill the context with all that we know.
  265. pCtxt->Reply = (PICMP_ECHO_REPLY)(startAddress + AlignSizeof(APC_CONTEXT));
  266. pCtxt->ReplySize = RCV_BUF_SIZE;
  267. pCtxt->DestinationAddress = DestAddr;
  268. pCtxt->Status = 0;
  269. pCtxt->Context = Context;
  270. // enqueue this guy.
  271. StartProducer(&IcmpProdConsSynchObj);
  272. LOCK_REQUESTS_LIST();
  273. InsertTailList(&IcmpRequestsList, &pCtxt->IcmpRequestsList);
  274. UNLOCK_REQUESTS_LIST();
  275. EndProducer(&IcmpProdConsSynchObj);
  276. // Signal the Requests loop.
  277. BoolStatus = SetEvent(IcmpRequestsEvent);
  278. ASSERT( TRUE == BoolStatus );
  279. return ERROR_SUCCESS;
  280. }
  281. //--------------------------------------------------------------------------------
  282. // The following functions handle the the end that sends ICMP echoes.
  283. //--------------------------------------------------------------------------------
  284. VOID static
  285. HandleRequestsEvent( // Process every request for ICMP echo.
  286. VOID
  287. ) {
  288. PAPC_CONTEXT ApcContext;
  289. PLIST_ENTRY listEntry;
  290. DWORD Status;
  291. LOCK_REQUESTS_LIST();
  292. while( !IsListEmpty( &IcmpRequestsList ) ) {
  293. // retrive the first element in the list
  294. ApcContext = CONTAINING_RECORD(IcmpRequestsList.Flink, APC_CONTEXT, IcmpRequestsList);
  295. RemoveEntryList(&ApcContext->IcmpRequestsList);
  296. UNLOCK_REQUESTS_LIST();
  297. // Send an Icmp echo and return immediately..
  298. ApcContext->Status ++;
  299. Status = IcmpSendEcho2(
  300. IcmpHandle, // The handle to register APC and send echo
  301. NULL, // No event
  302. ApcRoutine, // The call back routine
  303. (LPVOID)ApcContext, // The first parameter to the callback routine
  304. ApcContext->DestinationAddress, // The address being PING'ed
  305. SEND_MESSAGE,
  306. (WORD)strlen(SEND_MESSAGE),
  307. NULL,
  308. (LPVOID)ApcContext->Reply,
  309. ApcContext->ReplySize,
  310. WAIT_TIME
  311. );
  312. if( FALSE == Status ) Status = GetLastError();
  313. else Status = ERROR_SUCCESS;
  314. // Since we queued an APC, we expect an STATUS_PENDING.
  315. if( ERROR_SUCCESS != Status && ERROR_IO_PENDING != Status ) {
  316. ASSERT(FALSE);
  317. LOCK_OUTPUT();
  318. ErrorPrint("IcmpSendEcho2:GetLastError: %d\n", Status);
  319. UNLOCK_OUTPUT();
  320. }
  321. LOCK_REQUESTS_LIST();
  322. }
  323. UNLOCK_REQUESTS_LIST();
  324. }
  325. // This function handles the Requests.. For each one, it just sends an IcmpEcho
  326. // asynchronously and returns back immediately. When the APC routine is called,
  327. // it would queue it up on the Replies list and then it would get processed...
  328. DWORD static // THREAD ENTRY
  329. LoopOnIcmpRequests( // Process pending requests for echo
  330. LPVOID Unused
  331. ) {
  332. DWORD Status;
  333. HANDLE WaitHandles[2];
  334. WaitHandles[0] = TerminateEvent;
  335. WaitHandles[1] = IcmpRequestsEvent;
  336. while( TRUE ) {
  337. Status = WaitForMultipleObjectsEx(
  338. sizeof(WaitHandles)/sizeof(WaitHandles[0]), // # of handles
  339. WaitHandles, // array of handles
  340. FALSE, // any one of them set
  341. INFINITE, // wait forever
  342. TRUE // allow APC's
  343. );
  344. if( WAIT_OBJECT_0 == Status ) break; // Termination
  345. if( WAIT_IO_COMPLETION == Status) continue;
  346. if( 1+WAIT_OBJECT_0 == Status ) {
  347. HandleRequestsEvent();
  348. continue;
  349. }
  350. ASSERT(FALSE);
  351. }
  352. return ERROR_SUCCESS;
  353. }
  354. //--------------------------------------------------------------------------------
  355. // Initialization, Cleanup routines.
  356. //--------------------------------------------------------------------------------
  357. DWORD // exported
  358. PingInit(
  359. VOID
  360. ) {
  361. DWORD ThreadId, Status;
  362. // Initialize all data vars.
  363. IcmpRepliesEvent = IcmpRequestsEvent = TerminateEvent = NULL;
  364. RepliesThreadHandle = RequestsThreadHandle = NULL;
  365. IcmpHandle = NULL;
  366. // Open IcmpHandle..
  367. IcmpHandle = IcmpCreateFile();
  368. if( NULL == IcmpHandle ) return GetLastError();
  369. // Initialize Lists.
  370. InitializeListHead(&IcmpRepliesList);
  371. InitializeListHead(&IcmpRequestsList);
  372. // Initialize Critical Sections.
  373. InitializeCriticalSection(&IcmpRepliesCritSect);
  374. InitializeCriticalSection(&IcmpRequestsCritSect);
  375. InitializeCriticalSection(&OutputCritSect);
  376. // Create Events,
  377. IcmpRepliesEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
  378. if( NULL == IcmpRepliesEvent ) return GetLastError();
  379. IcmpRequestsEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
  380. if( NULL == IcmpRequestsEvent ) return GetLastError();
  381. TerminateEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
  382. if( NULL == TerminateEvent ) return GetLastError();
  383. // Create producer-consumer synchronization object
  384. Status = InitializeProducerConsumer(
  385. &IcmpProdConsSynchObj,
  386. MAX_PENDING_REQUESTS,
  387. MAX_PENDING_REQUESTS
  388. );
  389. if( ERROR_SUCCESS != Status ) return Status;
  390. // Create Threads
  391. RepliesThreadHandle = CreateThread(
  392. (LPSECURITY_ATTRIBUTES)
  393. NULL, // No security information
  394. 0, // Stack size = same as default primary thread
  395. LoopOnIcmpReplies, // The function to call
  396. NULL, // No paramter needs to be passed to this function
  397. 0, // Flags: just start this thread right away
  398. &ThreadId // The return ThreadId value.
  399. );
  400. if( NULL == RepliesThreadHandle ) return GetLastError();
  401. RequestsThreadHandle = CreateThread(
  402. NULL, // No security information
  403. 0, // Stack size = same as default primary thread
  404. LoopOnIcmpRequests, // The function to call
  405. NULL, // No paramter needs to be passed to this function
  406. 0, // Flags: just start this thread right away
  407. &ThreadId // The return ThreadId value.
  408. );
  409. if( NULL == RequestsThreadHandle ) return GetLastError();
  410. return ERROR_SUCCESS;
  411. }
  412. VOID // exported
  413. PingCleanup(
  414. VOID
  415. ) {
  416. DWORD Status;
  417. BOOL BoolStatus;
  418. PAPC_CONTEXT ApcContext;
  419. PLIST_ENTRY listEntry;
  420. // Kill the replies and reqeusts threads after waiting for a while.
  421. // Kill the Replies and Requests ThreadHandle 's.
  422. if( NULL != RepliesThreadHandle || NULL != RequestsThreadHandle ) {
  423. // ASSERT ( NULL != TerminateEvent )
  424. Terminating = TRUE;
  425. SetEvent(TerminateEvent);
  426. if( NULL != RepliesThreadHandle ) {
  427. Status = WaitForSingleObject(
  428. RepliesThreadHandle,
  429. THREAD_KILL_TIME
  430. );
  431. if( WAIT_OBJECT_0 != Status ) {
  432. // did not succeed in stopping the thread..
  433. BoolStatus = TerminateThread(
  434. RepliesThreadHandle,
  435. 0xFF
  436. );
  437. ASSERT(BoolStatus);
  438. }
  439. CloseHandle(RepliesThreadHandle);
  440. }
  441. if( NULL != RequestsThreadHandle ) {
  442. Status = WaitForSingleObject(
  443. RequestsThreadHandle,
  444. THREAD_KILL_TIME
  445. );
  446. if( WAIT_OBJECT_0 != Status ) {
  447. // did not succeed in stopping the thread..
  448. BoolStatus = TerminateThread(
  449. RequestsThreadHandle,
  450. 0xFF
  451. );
  452. ASSERT(BoolStatus);
  453. }
  454. CloseHandle(RequestsThreadHandle);
  455. }
  456. }
  457. // Close Event handles.
  458. CloseHandle(IcmpRepliesEvent);
  459. CloseHandle(IcmpRequestsEvent);
  460. CloseHandle(TerminateEvent);
  461. // Destroy producer consumer synchronization object
  462. DestroyProducerConsumer(&IcmpProdConsSynchObj);
  463. // Freeup all elements of lists..
  464. while( !IsListEmpty( &IcmpRepliesList ) ) {
  465. // retrive the first element in the list
  466. ApcContext = CONTAINING_RECORD(IcmpRepliesList.Flink, APC_CONTEXT, IcmpRepliesList);
  467. RemoveEntryList(&ApcContext->IcmpRepliesList);
  468. DhcpFreeMemory(ApcContext);
  469. }
  470. while( !IsListEmpty( &IcmpRequestsList ) ) {
  471. // retrive the first element in the list
  472. ApcContext = CONTAINING_RECORD(IcmpRequestsList.Flink, APC_CONTEXT, IcmpRequestsList);
  473. RemoveEntryList(&ApcContext->IcmpRequestsList);
  474. DhcpFreeMemory(ApcContext);
  475. }
  476. // Close Icmp handle
  477. CloseHandle(IcmpHandle);
  478. // Destroy critical sections
  479. DeleteCriticalSection(&IcmpRepliesCritSect);
  480. DeleteCriticalSection(&IcmpRequestsCritSect);
  481. DeleteCriticalSection(&OutputCritSect);
  482. }
  483. #ifdef PING_TEST
  484. //--------------------------------------------------------------------------------
  485. // Test module. This exercises the above functions.
  486. //--------------------------------------------------------------------------------
  487. DWORD SA, EA;
  488. DWORD
  489. TestPing(
  490. LPSTR StartAddrString,
  491. LPSTR EndAddrString
  492. ) {
  493. IPAddr i, StartAddr, EndAddr;
  494. DWORD Status;
  495. BOOL BoolStatus;
  496. HANDLE ThreadHandle;
  497. StartAddr = SA = inet_addr(StartAddrString);
  498. EndAddr = EA = inet_addr(EndAddrString);
  499. Status = PingInit();
  500. if( ERROR_SUCCESS != Status ) return Status;
  501. for( i = htonl(StartAddr); i <= htonl(EndAddr); i ++ ) {
  502. Status = DoIcmpRequest(ntohl(i), NULL);
  503. ASSERT( ERROR_SUCCESS == Status );
  504. }
  505. LOCK_OUTPUT();
  506. printf("Done\n");
  507. UNLOCK_OUTPUT();
  508. // Sleep for a while and then signal termination...
  509. Sleep(30000);
  510. // Give the threads time to die?
  511. PingCleanup();
  512. return ERROR_SUCCESS;
  513. }
  514. //--------------------------------------------------------------------------------
  515. // Main function. Just does the test routine.
  516. //--------------------------------------------------------------------------------
  517. VOID _cdecl
  518. main(
  519. int argc,
  520. char *argv[]
  521. ) {
  522. DWORD Status;
  523. if( argc != 3 ) {
  524. fprintf(stderr, "\nUsage: %s start-ip-addr end-ip-addr\n", argv[0]);
  525. exit(1);
  526. }
  527. Status = TestPing(argv[1], argv[2]);
  528. if( ERROR_SUCCESS != Status ) {
  529. fprintf(stderr, "Error: %d\n", Status);
  530. }
  531. }
  532. #endif PING_TEST
  533. //--------------------------------------------------------------------------------
  534. // End of file.
  535. //--------------------------------------------------------------------------------
  536. // This will really be defined elsewhere.. until then.
  537. VOID
  538. HandleIcmpResult( // Handle a processed ICMP packet.
  539. IPAddr DestAddr, // Attempted dest address
  540. BOOL Status, // Non-zero=> Dest reachable
  541. LPVOID Context // Whatever was passed to DoIcmpRe..
  542. ) {
  543. LOCK_OUTPUT();
  544. printf("Ping[%s] does %s exist\n",
  545. inet_ntoa(*(struct in_addr *)&DestAddr),
  546. Status? "" : "not" );
  547. UNLOCK_OUTPUT();
  548. }