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