2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS ping utility
4 * FILE: apps/net/ping/ping.c
5 * PURPOSE: Network test utility
6 * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
8 * CSH 01/09/2000 Created
19 /* FIXME: Where should this be? */
23 #define CopyMemory(Destination, Source, Length) memcpy(Destination, Source, Length);
25 /* Should be in the header files somewhere (exported by ntdll.dll) */
26 long atol(const char *str
);
29 typedef long long __int64
;
32 char * _i64toa(__int64 value
, char *string
, int radix
);
40 /* General ICMP constants */
41 #define ICMP_MINSIZE 8 /* Minimum ICMP packet size */
42 #define ICMP_MAXSIZE 65535 /* Maximum ICMP packet size */
44 /* ICMP message types */
45 #define ICMPMSG_ECHOREQUEST 8 /* ICMP ECHO request message */
46 #define ICMPMSG_ECHOREPLY 0 /* ICMP ECHO reply message */
50 /* IPv4 header structure */
51 typedef struct _IPv4_HEADER
{
53 unsigned char Version
:4;
55 unsigned short Length
;
57 unsigned short FragFlags
;
59 unsigned char Protocol
;
60 unsigned short Checksum
;
61 unsigned int SrcAddress
;
62 unsigned int DstAddress
;
63 } IPv4_HEADER
, *PIPv4_HEADER
;
65 /* ICMP echo request/reply header structure */
66 typedef struct _ICMP_HEADER
{
69 unsigned short Checksum
;
71 unsigned short SeqNum
;
72 } ICMP_HEADER
, *PICMP_HEADER
;
74 typedef struct _ICMP_ECHO_PACKET
{
76 LARGE_INTEGER Timestamp
;
77 } ICMP_ECHO_PACKET
, *PICMP_ECHO_PACKET
;
83 BOOL ResolveAddresses
;
85 UINT DataSize
; /* ICMP echo request data size */
100 LARGE_INTEGER MinRTT
; /* Minimum round trip time in microseconds */
101 LARGE_INTEGER MaxRTT
;
102 LARGE_INTEGER SumRTT
;
103 LARGE_INTEGER AvgRTT
;
104 LARGE_INTEGER TicksPerMs
; /* Ticks per millisecond */
105 LARGE_INTEGER TicksPerUs
; /* Ticks per microsecond */
106 BOOL UsePerformanceCounter
;
109 /* Display the contents of a buffer */
110 static VOID
DisplayBuffer(
117 printf("Buffer (0x%p) Size (0x%lX).\n", Buffer
, Size
);
120 for (i
= 0; i
< Size
; i
++) {
124 printf("%02X ", (p
[i
]) & 0xFF);
129 /* Display usage information on screen */
130 static VOID
Usage(VOID
)
132 printf("\nUsage: ping [-t] [-n count] [-l size] [-w timeout] destination-host\n\n");
133 printf("Options:\n");
134 printf(" -t Ping the specified host until stopped.\n");
135 printf(" To stop - type Control-C.\n");
136 printf(" -n count Number of echo requests to send.\n");
137 printf(" -l size Send buffer size.\n");
138 printf(" -w timeout Timeout in milliseconds to wait for each reply.\n\n");
141 /* Reset configuration to default values */
142 static VOID
Reset(VOID
)
144 LARGE_INTEGER PerformanceCounterFrequency
;
147 ResolveAddresses
= FALSE
;
150 DontFragment
= FALSE
;
154 UsePerformanceCounter
= QueryPerformanceFrequency(&PerformanceCounterFrequency
);
156 if (UsePerformanceCounter
) {
157 /* Performance counters may return incorrect results on some multiprocessor
158 platforms so we restrict execution on the first processor. This may fail
159 on Windows NT so we fall back to GetCurrentTick() for timing */
160 if (SetThreadAffinityMask (GetCurrentThread(), 1) == 0) {
161 UsePerformanceCounter
= FALSE
;
164 /* Convert frequency to ticks per millisecond */
165 TicksPerMs
.QuadPart
= PerformanceCounterFrequency
.QuadPart
/ 1000;
166 /* And to ticks per microsecond */
167 TicksPerUs
.QuadPart
= PerformanceCounterFrequency
.QuadPart
/ 1000000;
169 if (!UsePerformanceCounter
) {
170 /* 1 tick per millisecond for GetCurrentTick() */
171 TicksPerMs
.QuadPart
= 1;
172 /* GetCurrentTick() cannot handle microseconds */
173 TicksPerUs
.QuadPart
= 1;
177 /* Return ULONG in a string */
178 static ULONG
GetULONG(LPSTR String
)
184 Length
= (UINT
)_tcslen(String
);
185 while ((i
< Length
) && ((String
[i
] < '0') || (String
[i
] > '9'))) i
++;
186 if ((i
>= Length
) || ((String
[i
] < '0') || (String
[i
] > '9'))) {
187 InvalidOption
= TRUE
;
190 Value
= (ULONG
)atol(&String
[i
]);
195 /* Return ULONG in a string. Try next paramter if not successful */
196 static ULONG
GetULONG2(LPSTR String1
, LPSTR String2
, PINT i
)
200 Value
= GetULONG(String1
);
202 InvalidOption
= FALSE
;
203 if (String2
[0] != '-') {
204 Value
= GetULONG(String2
);
213 /* Parse command line parameters */
214 static BOOL
ParseCmdline(int argc
, char* argv
[])
220 // lstrcpy(TargetName, "127.0.0.1");
230 InvalidOption
= FALSE
;
232 for (i
= 1; i
< argc
; i
++) {
233 if (argv
[i
][0] == '-') {
234 switch (argv
[i
][1]) {
235 case 't': NeverStop
= TRUE
; break;
236 case 'a': ResolveAddresses
= TRUE
; break;
237 case 'n': PingCount
= GetULONG2(&argv
[i
][2], argv
[i
+ 1], &i
); break;
239 DataSize
= GetULONG2(&argv
[i
][2], argv
[i
+ 1], &i
);
240 if ((DataSize
< 0) || (DataSize
> ICMP_MAXSIZE
- sizeof(ICMP_ECHO_PACKET
))) {
241 printf("Bad value for option -l, valid range is from 0 to %d.\n",
242 ICMP_MAXSIZE
- sizeof(ICMP_ECHO_PACKET
));
246 case 'f': DontFragment
= TRUE
; break;
247 case 'i': TTLValue
= GetULONG2(&argv
[i
][2], argv
[i
+ 1], &i
); break;
248 case 'v': TOSValue
= GetULONG2(&argv
[i
][2], argv
[i
+ 1], &i
); break;
249 case 'w': Timeout
= GetULONG2(&argv
[i
][2], argv
[i
+ 1], &i
); break;
251 printf("Bad option %s.\n", argv
[i
]);
256 printf("Bad option format %s.\n", argv
[i
]);
261 printf("Bad parameter %s.\n", argv
[i
]);
264 lstrcpy(TargetName
, argv
[i
]);
270 if ((!ShowUsage
) && (!FoundTarget
)) {
271 printf("Name or IP address of destination host must be specified.\n");
282 /* Calculate checksum of data */
283 static WORD
Checksum(PUSHORT data
, UINT size
)
289 size
-= sizeof(USHORT
);
293 sum
+= *(UCHAR
*)data
;
295 sum
= (sum
>> 16) + (sum
& 0xFFFF);
298 return (USHORT
)(~sum
);
301 /* Prepare to ping target */
302 static BOOL
Setup(VOID
)
304 WORD wVersionRequested
;
310 wVersionRequested
= MAKEWORD(2, 2);
312 Status
= WSAStartup(wVersionRequested
, &WsaData
);
314 printf("Could not initialize winsock dll.\n");
318 IcmpSock
= WSASocket(AF_INET
, SOCK_RAW
, IPPROTO_ICMP
, NULL
, 0, 0);
319 if (IcmpSock
== INVALID_SOCKET
) {
320 printf("Could not create socket (#%d).\n", WSAGetLastError());
324 ZeroMemory(&Target
, sizeof(Target
));
326 Addr
= inet_addr(TargetName
);
327 if (Addr
== INADDR_NONE
) {
328 phe
= gethostbyname(TargetName
);
330 printf("Unknown host %s.\n", TargetName
);
336 CopyMemory(&Target
.sin_addr
, phe
->h_addr
, phe
->h_length
);
338 Target
.sin_addr
.s_addr
= Addr
;
342 Target
.sin_family
= phe
->h_addrtype
;
344 Target
.sin_family
= AF_INET
;
347 TargetIP
= inet_ntoa(Target
.sin_addr
);
359 static VOID
Cleanup(VOID
)
361 if (IcmpSock
!= INVALID_SOCKET
)
362 closesocket(IcmpSock
);
367 static VOID
QueryTime(PLARGE_INTEGER Time
)
369 if (UsePerformanceCounter
) {
370 if (QueryPerformanceCounter(Time
) == 0) {
371 /* This should not happen, but we fall
372 back to GetCurrentTick() if it does */
373 Time
->u
.LowPart
= (ULONG
)GetTickCount();
374 Time
->u
.HighPart
= 0;
376 /* 1 tick per millisecond for GetCurrentTick() */
377 TicksPerMs
.QuadPart
= 1;
378 /* GetCurrentTick() cannot handle microseconds */
379 TicksPerUs
.QuadPart
= 1;
381 UsePerformanceCounter
= FALSE
;
384 Time
->u
.LowPart
= (ULONG
)GetTickCount();
385 Time
->u
.HighPart
= 0;
389 static VOID
TimeToMsString(LPSTR String
, LARGE_INTEGER Time
)
392 LARGE_INTEGER LargeTime
;
394 LargeTime
.QuadPart
= Time
.QuadPart
/ TicksPerMs
.QuadPart
;
396 _i64toa(LargeTime
.QuadPart
, Convstr
, 10);
397 strcpy(String
, Convstr
);
398 strcat(String
, "ms");
401 /* Locate the ICMP data and print it. Returns TRUE if the packet was good,
403 static BOOL
DecodeResponse(PCHAR buffer
, UINT size
, PSOCKADDR_IN from
)
405 PIPv4_HEADER IpHeader
;
406 PICMP_ECHO_PACKET Icmp
;
409 LARGE_INTEGER RelativeTime
;
410 LARGE_INTEGER LargeTime
;
413 IpHeader
= (PIPv4_HEADER
)buffer
;
415 IphLength
= IpHeader
->IHL
* 4;
417 if (size
< IphLength
+ ICMP_MINSIZE
) {
419 printf("Bad size (0x%X < 0x%X)\n", size
, IphLength
+ ICMP_MINSIZE
);
424 Icmp
= (PICMP_ECHO_PACKET
)(buffer
+ IphLength
);
426 if (Icmp
->Icmp
.Type
!= ICMPMSG_ECHOREPLY
) {
428 printf("Bad ICMP type (0x%X should be 0x%X)\n", Icmp
->Icmp
.Type
, ICMPMSG_ECHOREPLY
);
433 if (Icmp
->Icmp
.Id
!= (USHORT
)GetCurrentProcessId()) {
435 printf("Bad ICMP id (0x%X should be 0x%X)\n", Icmp
->Icmp
.Id
, (USHORT
)GetCurrentProcessId());
440 QueryTime(&LargeTime
);
442 RelativeTime
.QuadPart
= (LargeTime
.QuadPart
- Icmp
->Timestamp
.QuadPart
);
444 if ((RelativeTime
.QuadPart
/ TicksPerMs
.QuadPart
) < 1) {
449 TimeToMsString(Time
, RelativeTime
);
453 printf("Reply from %s: bytes=%d time%s%s TTL=%d\n", inet_ntoa(from
->sin_addr
),
454 size
- IphLength
- sizeof(ICMP_ECHO_PACKET
), Sign
, Time
, IpHeader
->TTL
);
455 if (RelativeTime
.QuadPart
< MinRTT
.QuadPart
|| !MinRTTSet
) {
456 MinRTT
.QuadPart
= RelativeTime
.QuadPart
;
459 if (RelativeTime
.QuadPart
> MaxRTT
.QuadPart
)
460 MaxRTT
.QuadPart
= RelativeTime
.QuadPart
;
462 SumRTT
.QuadPart
+= RelativeTime
.QuadPart
;
467 /* Send and receive one ping */
468 static BOOL
Ping(VOID
)
475 PICMP_ECHO_PACKET Packet
;
477 /* Account for extra space for IP header when packet is received */
478 Size
= DataSize
+ 128;
479 Buffer
= GlobalAlloc(0, Size
);
481 printf("Not enough free resources available.\n");
485 ZeroMemory(Buffer
, Size
);
486 Packet
= (PICMP_ECHO_PACKET
)Buffer
;
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;
495 /* Timestamp is part of data area */
496 QueryTime(&Packet
->Timestamp
);
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
);
504 /* Send ICMP echo request */
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)) {
514 printf("Sending packet\n");
515 DisplayBuffer(Buffer
, sizeof(ICMP_ECHO_PACKET
) + DataSize
);
519 Status
= sendto(IcmpSock
, Buffer
, sizeof(ICMP_ECHO_PACKET
) + DataSize
,
520 0, (SOCKADDR
*)&Target
, sizeof(Target
));
523 if (Status
== SOCKET_ERROR
) {
524 if (WSAGetLastError() == WSAEHOSTUNREACH
) {
525 printf("Destination host unreachable.\n");
527 printf("Could not transmit data (%d).\n", WSAGetLastError());
533 /* Expect to receive ICMP echo reply */
535 FD_SET(IcmpSock
, &Fds
);
536 Timeval
.tv_sec
= Timeout
/ 1000;
537 Timeval
.tv_usec
= Timeout
% 1000;
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
);
545 printf("Received packet\n");
546 DisplayBuffer(Buffer
, Status
);
550 if (Status
== SOCKET_ERROR
) {
551 if (WSAGetLastError() != WSAETIMEDOUT
) {
552 printf("Could not receive data (%d).\n", WSAGetLastError());
560 printf("Request timed out.\n");
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");
577 /* Program entry point */
578 int main(int argc
, char* argv
[])
587 if ((ParseCmdline(argc
, argv
)) && (Setup())) {
589 printf("\nPinging %s [%s] with %d bytes of data:\n\n",
590 TargetName
, TargetIP
, DataSize
);
593 while ((NeverStop
) || (Count
< PingCount
)) {
601 /* Calculate avarage round trip time */
602 if ((SentCount
- LostCount
) > 0) {
603 AvgRTT
.QuadPart
= SumRTT
.QuadPart
/ (SentCount
- LostCount
);
608 /* Calculate loss percent */
610 Count
= (SentCount
* 100) / LostCount
;
618 TimeToMsString(MinTime
, MinRTT
);
619 TimeToMsString(MaxTime
, MaxRTT
);
620 TimeToMsString(AvgTime
, AvgRTT
);
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
);