ffcdadb2d8c029ca7c1fb0f0ab1e0bd73185d3bd
[reactos.git] / reactos / apps / utils / net / ping / ping.c
1 /* $Id$
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS ping utility
5 * FILE: apps/net/ping/ping.c
6 * PURPOSE: Network test utility
7 * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
8 * REVISIONS:
9 * CSH 01/09/2000 Created
10 */
11 //#include <windows.h>
12 #include <winsock2.h>
13 #include <tchar.h>
14 #include <stdarg.h>
15 #include <string.h>
16 #include <stdio.h>
17 #ifndef _MSC_VER
18
19 #ifdef DBG
20 #undef DBG
21 #endif
22
23 /* FIXME: Where should this be? */
24 #ifdef CopyMemory
25 #undef CopyMemory
26 #endif
27 #define CopyMemory(Destination, Source, Length) memcpy(Destination, Source, Length);
28
29 /* Should be in the header files somewhere (exported by ntdll.dll) */
30 long atol(const char *str);
31
32 #ifndef __int64
33 typedef long long __int64;
34 #endif
35
36 char * _i64toa(__int64 value, char *string, int radix);
37
38 #endif
39
40
41 /* General ICMP constants */
42 #define ICMP_MINSIZE 8 /* Minimum ICMP packet size */
43 #define ICMP_MAXSIZE 65535 /* Maximum ICMP packet size */
44
45 /* ICMP message types */
46 #define ICMPMSG_ECHOREQUEST 8 /* ICMP ECHO request message */
47 #define ICMPMSG_ECHOREPLY 0 /* ICMP ECHO reply message */
48
49 #pragma pack(4)
50
51 /* IPv4 header structure */
52 typedef struct _IPv4_HEADER {
53 unsigned char IHL:4;
54 unsigned char Version:4;
55 unsigned char TOS;
56 unsigned short Length;
57 unsigned short Id;
58 unsigned short FragFlags;
59 unsigned char TTL;
60 unsigned char Protocol;
61 unsigned short Checksum;
62 unsigned int SrcAddress;
63 unsigned int DstAddress;
64 } IPv4_HEADER, *PIPv4_HEADER;
65
66 /* ICMP echo request/reply header structure */
67 typedef struct _ICMP_HEADER {
68 unsigned char Type;
69 unsigned char Code;
70 unsigned short Checksum;
71 unsigned short Id;
72 unsigned short SeqNum;
73 } ICMP_HEADER, *PICMP_HEADER;
74
75 typedef struct _ICMP_ECHO_PACKET {
76 ICMP_HEADER Icmp;
77 LARGE_INTEGER Timestamp;
78 } ICMP_ECHO_PACKET, *PICMP_ECHO_PACKET;
79
80 #pragma pack(1)
81
82 BOOL InvalidOption;
83 BOOL NeverStop;
84 BOOL ResolveAddresses;
85 UINT PingCount;
86 UINT DataSize; /* ICMP echo request data size */
87 BOOL DontFragment;
88 ULONG TTLValue;
89 ULONG TOSValue;
90 ULONG Timeout;
91 CHAR TargetName[256];
92 SOCKET IcmpSock;
93 SOCKADDR_IN Target;
94 LPSTR TargetIP;
95 FD_SET Fds;
96 TIMEVAL Timeval;
97 UINT CurrentSeqNum;
98 UINT SentCount;
99 UINT LostCount;
100 BOOL MinRTTSet;
101 LARGE_INTEGER MinRTT; /* Minimum round trip time in microseconds */
102 LARGE_INTEGER MaxRTT;
103 LARGE_INTEGER SumRTT;
104 LARGE_INTEGER AvgRTT;
105 LARGE_INTEGER TicksPerMs; /* Ticks per millisecond */
106 LARGE_INTEGER TicksPerUs; /* Ticks per microsecond */
107 BOOL UsePerformanceCounter;
108
109 #ifdef DBG
110 /* Display the contents of a buffer */
111 static VOID DisplayBuffer(
112 PVOID Buffer,
113 DWORD Size)
114 {
115 UINT i;
116 PCHAR p;
117
118 printf("Buffer (0x%p) Size (0x%lX).\n", Buffer, Size);
119
120 p = (PCHAR)Buffer;
121 for (i = 0; i < Size; i++) {
122 if (i % 16 == 0) {
123 printf("\n");
124 }
125 printf("%02X ", (p[i]) & 0xFF);
126 }
127 }
128 #endif /* DBG */
129
130 /* Display usage information on screen */
131 static VOID Usage(VOID)
132 {
133 printf("\nUsage: ping [-t] [-n count] [-l size] [-w timeout] destination-host\n\n");
134 printf("Options:\n");
135 printf(" -t Ping the specified host until stopped.\n");
136 printf(" To stop - type Control-C.\n");
137 printf(" -n count Number of echo requests to send.\n");
138 printf(" -l size Send buffer size.\n");
139 printf(" -w timeout Timeout in milliseconds to wait for each reply.\n\n");
140 }
141
142 /* Reset configuration to default values */
143 static VOID Reset(VOID)
144 {
145 LARGE_INTEGER PerformanceCounterFrequency;
146
147 NeverStop = FALSE;
148 ResolveAddresses = FALSE;
149 PingCount = 4;
150 DataSize = 32;
151 DontFragment = FALSE;
152 TTLValue = 128;
153 TOSValue = 0;
154 Timeout = 1000;
155 UsePerformanceCounter = QueryPerformanceFrequency(&PerformanceCounterFrequency);
156
157 if (UsePerformanceCounter) {
158 /* Performance counters may return incorrect results on some multiprocessor
159 platforms so we restrict execution on the first processor. This may fail
160 on Windows NT so we fall back to GetCurrentTick() for timing */
161 if (SetThreadAffinityMask (GetCurrentThread(), 1) == 0) {
162 UsePerformanceCounter = FALSE;
163 }
164
165 /* Convert frequency to ticks per millisecond */
166 TicksPerMs.QuadPart = PerformanceCounterFrequency.QuadPart / 1000;
167 /* And to ticks per microsecond */
168 TicksPerUs.QuadPart = PerformanceCounterFrequency.QuadPart / 1000000;
169 }
170 if (!UsePerformanceCounter) {
171 /* 1 tick per millisecond for GetCurrentTick() */
172 TicksPerMs.QuadPart = 1;
173 /* GetCurrentTick() cannot handle microseconds */
174 TicksPerUs.QuadPart = 1;
175 }
176 }
177
178 /* Return ULONG in a string */
179 static ULONG GetULONG(LPSTR String)
180 {
181 UINT i, Length;
182 ULONG Value;
183
184 i = 0;
185 Length = (UINT)_tcslen(String);
186 while ((i < Length) && ((String[i] < '0') || (String[i] > '9'))) i++;
187 if ((i >= Length) || ((String[i] < '0') || (String[i] > '9'))) {
188 InvalidOption = TRUE;
189 return 0;
190 }
191 Value = (ULONG)atol(&String[i]);
192
193 return Value;
194 }
195
196 /* Return ULONG in a string. Try next paramter if not successful */
197 static ULONG GetULONG2(LPSTR String1, LPSTR String2, PINT i)
198 {
199 ULONG Value;
200
201 Value = GetULONG(String1);
202 if (InvalidOption) {
203 InvalidOption = FALSE;
204 if (String2[0] != '-') {
205 Value = GetULONG(String2);
206 if (!InvalidOption)
207 *i += 1;
208 }
209 }
210
211 return Value;
212 }
213
214 /* Parse command line parameters */
215 static BOOL ParseCmdline(int argc, char* argv[])
216 {
217 INT i;
218 BOOL ShowUsage;
219 BOOL FoundTarget;
220 //#if 1
221 // lstrcpy(TargetName, "127.0.0.1");
222 // PingCount = 1;
223 // return TRUE;
224 //#endif
225 if (argc < 2) {
226 ShowUsage = TRUE;
227 } else {
228 ShowUsage = FALSE;
229 }
230 FoundTarget = FALSE;
231 InvalidOption = FALSE;
232
233 for (i = 1; i < argc; i++) {
234 if (argv[i][0] == '-') {
235 switch (argv[i][1]) {
236 case 't': NeverStop = TRUE; break;
237 case 'a': ResolveAddresses = TRUE; break;
238 case 'n': PingCount = GetULONG2(&argv[i][2], argv[i + 1], &i); break;
239 case 'l':
240 DataSize = GetULONG2(&argv[i][2], argv[i + 1], &i);
241 if ((DataSize < 0) || (DataSize > ICMP_MAXSIZE - sizeof(ICMP_ECHO_PACKET))) {
242 printf("Bad value for option -l, valid range is from 0 to %d.\n",
243 ICMP_MAXSIZE - sizeof(ICMP_ECHO_PACKET));
244 return FALSE;
245 }
246 break;
247 case 'f': DontFragment = TRUE; break;
248 case 'i': TTLValue = GetULONG2(&argv[i][2], argv[i + 1], &i); break;
249 case 'v': TOSValue = GetULONG2(&argv[i][2], argv[i + 1], &i); break;
250 case 'w': Timeout = GetULONG2(&argv[i][2], argv[i + 1], &i); break;
251 default:
252 printf("Bad option %s.\n", argv[i]);
253 Usage();
254 return FALSE;
255 }
256 if (InvalidOption) {
257 printf("Bad option format %s.\n", argv[i]);
258 return FALSE;
259 }
260 } else {
261 if (FoundTarget) {
262 printf("Bad parameter %s.\n", argv[i]);
263 return FALSE;
264 } else {
265 lstrcpy(TargetName, argv[i]);
266 FoundTarget = TRUE;
267 }
268 }
269 }
270
271 if ((!ShowUsage) && (!FoundTarget)) {
272 printf("Name or IP address of destination host must be specified.\n");
273 return FALSE;
274 }
275
276 if (ShowUsage) {
277 Usage();
278 return FALSE;
279 }
280 return TRUE;
281 }
282
283 /* Calculate checksum of data */
284 static WORD Checksum(PUSHORT data, UINT size)
285 {
286 ULONG sum = 0;
287
288 while (size > 1) {
289 sum += *data++;
290 size -= sizeof(USHORT);
291 }
292
293 if (size)
294 sum += *(UCHAR*)data;
295
296 sum = (sum >> 16) + (sum & 0xFFFF);
297 sum += (sum >> 16);
298
299 return (USHORT)(~sum);
300 }
301
302 /* Prepare to ping target */
303 static BOOL Setup(VOID)
304 {
305 WORD wVersionRequested;
306 WSADATA WsaData;
307 INT Status;
308 ULONG Addr;
309 PHOSTENT phe;
310
311 wVersionRequested = MAKEWORD(2, 2);
312
313 Status = WSAStartup(wVersionRequested, &WsaData);
314 if (Status != 0) {
315 printf("Could not initialize winsock dll.\n");
316 return FALSE;
317 }
318
319 IcmpSock = WSASocket(AF_INET, SOCK_RAW, IPPROTO_ICMP, NULL, 0, 0);
320 if (IcmpSock == INVALID_SOCKET) {
321 printf("Could not create socket (#%d).\n", WSAGetLastError());
322 return FALSE;
323 }
324
325 ZeroMemory(&Target, sizeof(Target));
326 phe = NULL;
327 Addr = inet_addr(TargetName);
328 if (Addr == INADDR_NONE) {
329 phe = gethostbyname(TargetName);
330 if (phe == NULL) {
331 printf("Unknown host %s.\n", TargetName);
332 return FALSE;
333 }
334 }
335
336 if (phe != NULL) {
337 CopyMemory(&Target.sin_addr, phe->h_addr, phe->h_length);
338 } else {
339 Target.sin_addr.s_addr = Addr;
340 }
341
342 if (phe != NULL) {
343 Target.sin_family = phe->h_addrtype;
344 } else {
345 Target.sin_family = AF_INET;
346 }
347
348 TargetIP = inet_ntoa(Target.sin_addr);
349 CurrentSeqNum = 0;
350 SentCount = 0;
351 LostCount = 0;
352 MinRTT.QuadPart = 0;
353 MaxRTT.QuadPart = 0;
354 SumRTT.QuadPart = 0;
355 MinRTTSet = FALSE;
356 return TRUE;
357 }
358
359 /* Close socket */
360 static VOID Cleanup(VOID)
361 {
362 if (IcmpSock != INVALID_SOCKET)
363 closesocket(IcmpSock);
364
365 WSACleanup();
366 }
367
368 static VOID QueryTime(PLARGE_INTEGER Time)
369 {
370 if (UsePerformanceCounter) {
371 if (QueryPerformanceCounter(Time) == 0) {
372 /* This should not happen, but we fall
373 back to GetCurrentTick() if it does */
374 Time->u.LowPart = (ULONG)GetTickCount();
375 Time->u.HighPart = 0;
376
377 /* 1 tick per millisecond for GetCurrentTick() */
378 TicksPerMs.QuadPart = 1;
379 /* GetCurrentTick() cannot handle microseconds */
380 TicksPerUs.QuadPart = 1;
381
382 UsePerformanceCounter = FALSE;
383 }
384 } else {
385 Time->u.LowPart = (ULONG)GetTickCount();
386 Time->u.HighPart = 0;
387 }
388 }
389
390 static VOID TimeToMsString(LPSTR String, LARGE_INTEGER Time)
391 {
392 CHAR Convstr[40];
393 LARGE_INTEGER LargeTime;
394
395 LargeTime.QuadPart = Time.QuadPart / TicksPerMs.QuadPart;
396
397 _i64toa(LargeTime.QuadPart, Convstr, 10);
398 strcpy(String, Convstr);
399 strcat(String, "ms");
400 }
401
402 /* Locate the ICMP data and print it. Returns TRUE if the packet was good,
403 FALSE if not */
404 static BOOL DecodeResponse(PCHAR buffer, UINT size, PSOCKADDR_IN from)
405 {
406 PIPv4_HEADER IpHeader;
407 PICMP_ECHO_PACKET Icmp;
408 UINT IphLength;
409 CHAR Time[100];
410 LARGE_INTEGER RelativeTime;
411 LARGE_INTEGER LargeTime;
412 CHAR Sign[1];
413
414 IpHeader = (PIPv4_HEADER)buffer;
415
416 IphLength = IpHeader->IHL * 4;
417
418 if (size < IphLength + ICMP_MINSIZE) {
419 #ifdef DBG
420 printf("Bad size (0x%X < 0x%X)\n", size, IphLength + ICMP_MINSIZE);
421 #endif /* DBG */
422 return FALSE;
423 }
424
425 Icmp = (PICMP_ECHO_PACKET)(buffer + IphLength);
426
427 if (Icmp->Icmp.Type != ICMPMSG_ECHOREPLY) {
428 #ifdef DBG
429 printf("Bad ICMP type (0x%X should be 0x%X)\n", Icmp->Icmp.Type, ICMPMSG_ECHOREPLY);
430 #endif /* DBG */
431 return FALSE;
432 }
433
434 if (Icmp->Icmp.Id != (USHORT)GetCurrentProcessId()) {
435 #ifdef DBG
436 printf("Bad ICMP id (0x%X should be 0x%X)\n", Icmp->Icmp.Id, (USHORT)GetCurrentProcessId());
437 #endif /* DBG */
438 return FALSE;
439 }
440
441 QueryTime(&LargeTime);
442
443 RelativeTime.QuadPart = (LargeTime.QuadPart - Icmp->Timestamp.QuadPart);
444
445 if ((RelativeTime.QuadPart / TicksPerMs.QuadPart) < 1) {
446 strcpy(Sign, "<");
447 strcpy(Time, "1ms");
448 } else {
449 strcpy(Sign, "=");
450 TimeToMsString(Time, RelativeTime);
451 }
452
453
454 printf("Reply from %s: bytes=%d time%s%s TTL=%d\n", inet_ntoa(from->sin_addr),
455 size - IphLength - sizeof(ICMP_ECHO_PACKET), Sign, Time, IpHeader->TTL);
456 if (RelativeTime.QuadPart < MinRTT.QuadPart) {
457 MinRTT.QuadPart = RelativeTime.QuadPart;
458 MinRTTSet = TRUE;
459 }
460 if (RelativeTime.QuadPart > MaxRTT.QuadPart)
461 MaxRTT.QuadPart = RelativeTime.QuadPart;
462 SumRTT.QuadPart += RelativeTime.QuadPart;
463
464 return TRUE;
465 }
466
467 /* Send and receive one ping */
468 static BOOL Ping(VOID)
469 {
470 INT Status;
471 SOCKADDR From;
472 INT Length;
473 PVOID Buffer;
474 UINT Size;
475 PICMP_ECHO_PACKET Packet;
476
477 /* Account for extra space for IP header when packet is received */
478 Size = DataSize + 128;
479 Buffer = GlobalAlloc(0, Size);
480 if (!Buffer) {
481 printf("Not enough free resources available.\n");
482 return FALSE;
483 }
484
485 ZeroMemory(Buffer, Size);
486 Packet = (PICMP_ECHO_PACKET)Buffer;
487
488 /* Assemble ICMP echo request packet */
489 Packet->Icmp.Type = ICMPMSG_ECHOREQUEST;
490 Packet->Icmp.Code = 0;
491 Packet->Icmp.Id = (USHORT)GetCurrentProcessId();
492 Packet->Icmp.SeqNum = (USHORT)CurrentSeqNum;
493 Packet->Icmp.Checksum = 0;
494
495 /* Timestamp is part of data area */
496 QueryTime(&Packet->Timestamp);
497
498 CopyMemory(Buffer, &Packet->Icmp, sizeof(ICMP_ECHO_PACKET) + DataSize);
499 /* Calculate checksum for ICMP header and data area */
500 Packet->Icmp.Checksum = Checksum((PUSHORT)&Packet->Icmp, sizeof(ICMP_ECHO_PACKET) + DataSize);
501
502 CurrentSeqNum++;
503
504 /* Send ICMP echo request */
505
506 FD_ZERO(&Fds);
507 FD_SET(IcmpSock, &Fds);
508 Timeval.tv_sec = Timeout / 1000;
509 Timeval.tv_usec = Timeout % 1000;
510 Status = select(0, NULL, &Fds, NULL, &Timeval);
511 if ((Status != SOCKET_ERROR) && (Status != 0)) {
512
513 #ifdef DBG
514 printf("Sending packet\n");
515 DisplayBuffer(Buffer, sizeof(ICMP_ECHO_PACKET) + DataSize);
516 printf("\n");
517 #endif /* DBG */
518
519 Status = sendto(IcmpSock, Buffer, sizeof(ICMP_ECHO_PACKET) + DataSize,
520 0, (SOCKADDR*)&Target, sizeof(Target));
521 SentCount++;
522 }
523 if (Status == SOCKET_ERROR) {
524 if (WSAGetLastError() == WSAEHOSTUNREACH) {
525 printf("Destination host unreachable.\n");
526 } else {
527 printf("Could not transmit data (%d).\n", WSAGetLastError());
528 }
529 GlobalFree(Buffer);
530 return FALSE;
531 }
532
533 /* Expect to receive ICMP echo reply */
534 FD_ZERO(&Fds);
535 FD_SET(IcmpSock, &Fds);
536 Timeval.tv_sec = Timeout / 1000;
537 Timeval.tv_usec = Timeout % 1000;
538
539 Status = select(0, &Fds, NULL, NULL, &Timeval);
540 if ((Status != SOCKET_ERROR) && (Status != 0)) {
541 Length = sizeof(From);
542 Status = recvfrom(IcmpSock, Buffer, Size, 0, &From, &Length);
543
544 #ifdef DBG
545 printf("Received packet\n");
546 DisplayBuffer(Buffer, Status);
547 printf("\n");
548 #endif /* DBG */
549 }
550 if (Status == SOCKET_ERROR) {
551 if (WSAGetLastError() != WSAETIMEDOUT) {
552 printf("Could not receive data (%d).\n", WSAGetLastError());
553 GlobalFree(Buffer);
554 return FALSE;
555 }
556 Status = 0;
557 }
558
559 if (Status == 0) {
560 printf("Request timed out.\n");
561 LostCount++;
562 GlobalFree(Buffer);
563 return TRUE;
564 }
565
566 if (!DecodeResponse(Buffer, Status, (PSOCKADDR_IN)&From)) {
567 /* FIXME: Wait again as it could be another ICMP message type */
568 printf("Request timed out (incomplete datagram received).\n");
569 LostCount++;
570 }
571
572 GlobalFree(Buffer);
573 return TRUE;
574 }
575
576
577 /* Program entry point */
578 int main(int argc, char* argv[])
579 {
580 UINT Count;
581 CHAR MinTime[20];
582 CHAR MaxTime[20];
583 CHAR AvgTime[20];
584
585 Reset();
586
587 if ((ParseCmdline(argc, argv)) && (Setup())) {
588
589 printf("\nPinging %s [%s] with %d bytes of data:\n\n",
590 TargetName, TargetIP, DataSize);
591
592 Count = 0;
593 while ((NeverStop) || (Count < PingCount)) {
594 Ping();
595 Sleep(Timeout);
596 Count++;
597 };
598
599 Cleanup();
600
601 /* Calculate avarage round trip time */
602 if ((SentCount - LostCount) > 0) {
603 AvgRTT.QuadPart = SumRTT.QuadPart / (SentCount - LostCount);
604 } else {
605 AvgRTT.QuadPart = 0;
606 }
607
608 /* Calculate loss percent */
609 if (LostCount > 0) {
610 Count = (SentCount * 100) / LostCount;
611 } else {
612 Count = 0;
613 }
614
615 if (!MinRTTSet)
616 MinRTT = MaxRTT;
617
618 TimeToMsString(MinTime, MinRTT);
619 TimeToMsString(MaxTime, MaxRTT);
620 TimeToMsString(AvgTime, AvgRTT);
621
622 /* Print statistics */
623 printf("\nPing statistics for %s:\n", TargetIP);
624 printf(" Packets: Sent = %d, Received = %d, Lost = %d (%d%% loss),\n",
625 SentCount, SentCount - LostCount, LostCount, Count);
626 printf("Approximate round trip times in milli-seconds:\n");
627 printf(" Minimum = %s, Maximum = %s, Average = %s\n",
628 MinTime, MaxTime, AvgTime);
629 }
630 return 0;
631 }
632
633 /* EOF */